understanding_rl_vision/svelte3/compiling.py (189 lines of code) (raw):
from contextlib import contextmanager
import json
import tempfile
import subprocess
import os
from lucid.misc.io.writing import write_handle
from lucid.misc.io.reading import read_handle
from .json_encoding import encoder
_temp_config_dir = tempfile.mkdtemp(prefix="svelte3_")
_default_js_name = "App"
_default_div_id = "appdiv"
class CompileError(Exception):
pass
def replace_file_extension(path, extension):
"""Replace the file extension of a path with a new extension."""
if not extension.startswith("."):
extension = "." + extension
dir_, filename = os.path.split(path)
if not filename.endswith(extension):
filename = filename.rsplit(".", 1)[0]
return os.path.join(dir_, filename + extension)
@contextmanager
def use_cwd(dir_):
"""Context manager for working in a different directory."""
cwd = os.getcwd()
try:
os.chdir(dir_)
yield
finally:
os.chdir(cwd)
def shell_command(command, **kwargs):
"""Wrapper around subprocess.check_output. Should be used with care:
https://docs.python.org/3/library/subprocess.html#security-considerations
"""
try:
return subprocess.check_output(
command,
stderr=subprocess.STDOUT,
shell=True,
universal_newlines=True,
**kwargs
).strip("\n")
except subprocess.CalledProcessError as exn:
raise CompileError(
"Command '%s' failed with output:\n\n%s" % (command, exn.output)
) from exn
def compile_js(svelte_path, js_path=None, *, js_name=None, js_lint=None):
"""Compile Svelte to JavaScript.
Arguments:
svelte_path: path to input Svelte file
js_path: path to output JavaScript file
defaults to svelte_path with a new .js suffix
js_name: name of JavaScript global variable
defaults to _default_js_name
js_lint: whether to use eslint
defaults to True
"""
if js_path is None:
js_path = replace_file_extension(svelte_path, ".js")
if js_name is None:
js_name = _default_js_name
if js_lint is None:
js_lint = True
eslint_config_fd, eslint_config_path = tempfile.mkstemp(
suffix=".config.json", prefix="eslint_", dir=_temp_config_dir, text=True
)
eslint_config_path = os.path.abspath(eslint_config_path)
rollup_config_fd, rollup_config_path = tempfile.mkstemp(
suffix=".config.js", prefix="rollup_", dir=_temp_config_dir, text=True
)
rollup_config_path = os.path.abspath(rollup_config_path)
svelte_dir = os.path.dirname(svelte_path) or os.curdir
svelte_relpath = os.path.relpath(svelte_path, start=svelte_dir)
js_relpath = os.path.relpath(js_path, start=svelte_dir)
with open(eslint_config_fd, "w") as eslint_config_file:
json.dump(
{
"env": {"browser": True, "es6": True},
"extends": "eslint:recommended",
"globals": {"Atomics": "readonly", "SharedArrayBuffer": "readonly"},
"parserOptions": {"ecmaVersion": 2018, "sourceType": "module"},
"plugins": ["svelte3"],
"overrides": [{"files": ["*.svelte"], "processor": "svelte3/svelte3"}],
"rules": {},
},
eslint_config_file,
)
with open(rollup_config_fd, "w") as rollup_config_file:
rollup_config_file.write(
"""import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import { eslint } from 'rollup-plugin-eslint';
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import path from 'path';
export default {
input: '"""
+ svelte_relpath
+ """',
output: {
file: '"""
+ js_relpath
+ """',
format: 'iife',
name: '"""
+ js_name
+ """'
},
plugins: [
eslint({
include: ['**'],
"""
+ ("" if js_lint else "exclude: ['**'],")
+ """
configFile: '"""
+ eslint_config_path
+ """'
}),
svelte({
include: ['"""
+ svelte_relpath
+ """', '**/*.svelte']
}),
resolve({
customResolveOptions: {
paths: process.env.NODE_PATH.split( /[;:]/ )
}
}),
commonjs(),
babel({
include: ['**', path.resolve(process.env.NODE_PATH, 'svelte/**')],
extensions: ['.js', '.jsx', '.es6', '.es', '.mjs', '.svelte'],
babelrc: false,
cwd: process.env.NODE_PATH,
presets: [['@babel/preset-env', {useBuiltIns: 'usage', corejs: 3}]]
})
]
}
"""
)
with use_cwd(os.path.dirname(os.path.realpath(__file__)) or os.curdir):
try:
npm_root = shell_command("npm root --quiet")
except CompileError as exn:
raise CompileError(
"Unable to find npm root.\nHave you installed Node.js?"
) from exn
try:
shell_command("npm ls")
except CompileError as exn:
shell_command("npm install")
with use_cwd(svelte_dir):
env = os.environ.copy()
env["PATH"] = os.path.join(npm_root, ".bin") + ":" + env["PATH"]
env["NODE_PATH"] = npm_root
command_output = shell_command("rollup -c " + rollup_config_path, env=env)
return {
"js_path": js_path,
"js_name": js_name,
"command_output": command_output,
}
def compile_html(
input_path,
html_path=None,
*,
props=None,
precision=None,
title=None,
div_id=None,
inline_js=None,
svelte_to_js=None,
js_path=None,
js_name=None,
js_lint=None
):
"""Compile Svelte or JavaScript to HTML.
Arguments:
input_path: path to input Svelte or JavaScript file
html_path: path to output HTML file
defaults to input_path with a new .html suffix
props: JSON-serializable object to pass to Svelte script
defaults to an empty object
precision: number of significant figures to round numpy arrays to
defaults to no rounding
title: title of HTML page
defaults to html_path filename without suffix
div_id: HTML id of div containing Svelte component
defaults to _default_div_id
inline_js: whether to insert the JavaScript into the HTML page inline
defaults to svelte_to_js
svelte_to_js: whether to first compile from Svelte to JavaScript
defaults to whether input_path doesn't have a .js suffix
js_path: path to output JavaScript file if compiling from Svelte
and not inserting the JavaScript inline
defaults to compile_js default
js_name: name of JavaScript global variable
should match existing name if compiling from JavaScript
defaults to _default_js_name
js_lint: whether to use eslint if compiling from Svelte
defaults to compile_js default
"""
if html_path is None:
html_path = replace_file_extension(input_path, ".html")
if props is None:
props = {}
if title is None:
title = os.path.basename(html_path).rsplit(".", 1)[0]
if div_id is None:
div_id = _default_div_id
if svelte_to_js is None:
svelte_to_js = not input_path.endswith(".js")
if inline_js is None:
inline_js = svelte_to_js
if svelte_to_js:
if inline_js:
if js_path is None:
js_path = replace_file_extension(input_path, ".js")
prefix = "svelte_" + os.path.basename(js_path)
if prefix.endswith(".js"):
prefix = prefix[:-3]
_, js_path = tempfile.mkstemp(
suffix=".js", prefix=prefix + "_", dir=_temp_config_dir, text=True
)
try:
compile_js_result = compile_js(
input_path, js_path, js_name=js_name, js_lint=js_lint
)
except CompileError as exn:
raise CompileError(
"Unable to compile Svelte source.\n"
"See the above advice or try supplying pre-compiled JavaScript."
) from exn
js_path = compile_js_result["js_path"]
js_name = compile_js_result["js_name"]
command_output = compile_js_result["command_output"]
else:
js_path = input_path
if js_name is None:
js_name = _default_js_name
command_output = None
if inline_js:
with read_handle(js_path, cache=False, mode="r") as js_file:
js_code = js_file.read().rstrip("\n")
js_html = "<script>\n" + js_code + "\n </script>"
js_path = None
else:
js_relpath = os.path.relpath(js_path, start=os.path.dirname(html_path))
js_html = '<script src="' + js_relpath + '"></script>'
with write_handle(html_path, "w") as html_file:
html_file.write(
"""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>"""
+ title
+ '''</title>
</head>
<body>
<div id="'''
+ div_id
+ """"></div>
"""
+ js_html
+ """
<script>
var app = new """
+ js_name
+ """({
target: document.querySelector("#"""
+ div_id
+ """"),
props: """
+ json.dumps(props, cls=encoder(precision=precision))
+ """
});
</script>
</body>
</html>"""
)
return {
"html_path": html_path,
"js_path": js_path if svelte_to_js else None,
"title": title,
"div_id": div_id,
"js_name": js_name,
"command_output": command_output,
}