aboutsummaryrefslogtreecommitdiffstats
path: root/build/plugins/lib/nots
diff options
context:
space:
mode:
authormiripiruni <miripiruni@yandex-team.ru>2022-06-27 19:50:44 +0300
committermiripiruni <miripiruni@yandex-team.ru>2022-06-27 19:50:44 +0300
commitaf185767aee9f3fdaa37975bec76bbcaf508af89 (patch)
treeabd59a64baec79e60c373011979b723d874c7f53 /build/plugins/lib/nots
parentb1f45896cfd344d7e216f14a0ae66d385430c5b3 (diff)
downloadydb-af185767aee9f3fdaa37975bec76bbcaf508af89.tar.gz
feat: TS_BUNDLE with dependencies
ref:e744df2a7b177057e6bb9b0fc888a8ed0c56ff0c
Diffstat (limited to 'build/plugins/lib/nots')
-rw-r--r--build/plugins/lib/nots/typescript/__init__.py6
-rw-r--r--build/plugins/lib/nots/typescript/ts_bundle_wrapper.py87
-rw-r--r--build/plugins/lib/nots/typescript/ts_config.py141
-rw-r--r--build/plugins/lib/nots/typescript/ts_errors.py19
-rw-r--r--build/plugins/lib/nots/typescript/tsc_wrapper.py155
5 files changed, 254 insertions, 154 deletions
diff --git a/build/plugins/lib/nots/typescript/__init__.py b/build/plugins/lib/nots/typescript/__init__.py
index 4684004183..f7cc9d9c83 100644
--- a/build/plugins/lib/nots/typescript/__init__.py
+++ b/build/plugins/lib/nots/typescript/__init__.py
@@ -1,7 +1,11 @@
-from .tsc_wrapper import TscWrapper, TsConfig, TsValidationError
+from .ts_bundle_wrapper import TsBundleWrapper
+from .ts_config import TsConfig
+from .ts_errors import TsValidationError
+from .tsc_wrapper import TscWrapper
__all__ = [
"TscWrapper",
+ "TsBundleWrapper",
"TsConfig",
"TsValidationError",
]
diff --git a/build/plugins/lib/nots/typescript/ts_bundle_wrapper.py b/build/plugins/lib/nots/typescript/ts_bundle_wrapper.py
new file mode 100644
index 0000000000..43a02412ff
--- /dev/null
+++ b/build/plugins/lib/nots/typescript/ts_bundle_wrapper.py
@@ -0,0 +1,87 @@
+import os
+import shutil
+import subprocess
+import tarfile
+
+from ..package_manager import constants
+from .ts_config import TsConfig
+from .ts_errors import TsCompilationError
+
+
+class TsBundleWrapper(object):
+ def __init__(self, build_root, build_path, sources_path, nodejs_bin_path, script_path, ts_config_path, webpack_config_path, webpack_resource):
+ self.build_root = build_root
+ self.build_path = build_path
+ self.sources_path = sources_path
+ self.nodejs_bin_path = nodejs_bin_path
+ self.script_path = script_path
+ self.ts_config_curpath = ts_config_path
+ self.ts_config_binpath = os.path.join(build_path, os.path.basename(ts_config_path))
+ self.webpack_config_curpath = webpack_config_path
+ self.webpack_config_binpath = os.path.join(build_path, os.path.basename(webpack_config_path))
+ self.webpack_resource = webpack_resource
+
+ def compile(self):
+ self._prepare_dependencies()
+ self._build_configs()
+ self._exec_webpack()
+ self._pack_bundle()
+
+ def _prepare_dependencies(self):
+ self._copy_package_json()
+ self._unpack_node_modules()
+
+ def _copy_package_json(self):
+ # TODO: Validate "main" and "files" - they should include files from the output directory.
+ shutil.copyfile(
+ os.path.join(self.sources_path, constants.PACKAGE_JSON_FILENAME),
+ os.path.join(self.build_path, constants.PACKAGE_JSON_FILENAME),
+ )
+
+ def _unpack_node_modules(self):
+ nm_bundle_path = os.path.join(self.build_path, constants.NODE_MODULES_BUNDLE_FILENAME)
+ if os.path.isfile(nm_bundle_path):
+ with tarfile.open(nm_bundle_path) as tf:
+ tf.extractall(os.path.join(self.build_path, "node_modules"))
+
+ def _build_configs(self):
+ shutil.copyfile(
+ self.webpack_config_curpath,
+ self.webpack_config_binpath
+ )
+
+ config = TsConfig.load(self.ts_config_curpath)
+ config.validate()
+ config.transform_paths(
+ build_path=self.build_path,
+ sources_path=self.sources_path,
+ )
+
+ config.path = self.ts_config_binpath
+ config.write()
+
+ def _exec_webpack(self):
+ custom_envs = {
+ "WEBPACK_CONFIG": self.webpack_config_binpath,
+ "CURDIR": self.sources_path,
+ "BINDIR": self.build_path,
+ "NODE_MODULES_DIRS": self.webpack_resource
+ }
+
+ p = subprocess.Popen(
+ [self.nodejs_bin_path, self.script_path, "--config", self.webpack_config_binpath],
+ cwd=self.build_path,
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=custom_envs,
+ )
+ stdout, stderr = p.communicate()
+
+ if p.returncode != 0:
+ raise TsCompilationError(p.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"))
+
+ def _pack_bundle(self):
+ with tarfile.open(self.build_path + "/bundle.tar", "w") as tf:
+ tf.add(self.build_path + "/bundle")
+ tf.close()
diff --git a/build/plugins/lib/nots/typescript/ts_config.py b/build/plugins/lib/nots/typescript/ts_config.py
new file mode 100644
index 0000000000..b42ab221d0
--- /dev/null
+++ b/build/plugins/lib/nots/typescript/ts_config.py
@@ -0,0 +1,141 @@
+import os
+import json
+
+from .ts_errors import TsError, TsValidationError
+
+
+class TsConfig(object):
+ @classmethod
+ def load(cls, path):
+ """
+ :param path: tsconfig.json path
+ :type path: str
+ :rtype: TsConfig
+ """
+ tsconfig = cls(path)
+ tsconfig.read()
+
+ return tsconfig
+
+ def __init__(self, path):
+ if not os.path.isabs(path):
+ raise TypeError("Absolute path required, given: {}".format(path))
+
+ self.path = path
+ self.data = {}
+
+ def read(self):
+ try:
+ with open(self.path) as f:
+ self.data = json.load(f)
+ except Exception as e:
+ raise TsError("Failed to read tsconfig {}: {}".format(self.path, e))
+
+ def get_or_create_compiler_options(self):
+ """
+ Returns ref to the "compilerOptions" dict.
+ :rtype: dict
+ """
+ opts = self.data.get("compilerOptions")
+ if opts is None:
+ opts = {}
+ self.data["compilerOptions"] = opts
+
+ return opts
+
+ def compiler_option(self, name, default=None):
+ """
+ :param name: option key
+ :type name: str
+ :param default: default value
+ :type default: mixed
+ :rtype: mixed
+ """
+ return self.get_or_create_compiler_options().get(name, default)
+
+ def validate(self):
+ """
+ Checks whether the config is compatible with current toolchain.
+ """
+ opts = self.get_or_create_compiler_options()
+ errors = []
+ root_dir = opts.get("rootDir")
+ out_dir = opts.get("outDir")
+ config_dir = os.path.dirname(self.path)
+ is_mod_subdir = lambda p: not os.path.isabs(p) and os.path.normpath(os.path.join(config_dir, p)).startswith(config_dir)
+
+ if root_dir is None:
+ errors.append("'rootDir' option is required")
+ elif not is_mod_subdir(root_dir):
+ errors.append("'rootDir' should be a subdirectory of the module")
+
+ if out_dir is None:
+ errors.append("'outDir' option is required")
+ elif not is_mod_subdir(out_dir):
+ errors.append("'outDir' should be a subdirectory of the module")
+
+ if opts.get("outFile") is not None:
+ errors.append("'outFile' option is not supported")
+
+ if opts.get("preserveSymlinks"):
+ errors.append("'preserveSymlinks' option is not supported due to pnpm limitations")
+
+ if opts.get("rootDirs") is not None:
+ errors.append("'rootDirs' option is not supported, relative imports should have single root")
+
+ if self.data.get("files") is not None:
+ errors.append("'files' option is not supported, use 'include'")
+
+ if self.data.get("references") is not None:
+ errors.append("composite builds are not supported, use peerdirs in ya.make instead of 'references' option")
+
+ if len(errors):
+ raise TsValidationError(self.path, errors)
+
+ def transform_paths(self, build_path, sources_path):
+ """
+ Updates config with correct abs paths.
+ All source files/dirs will be mapped to `sources_path`, output files/dirs will be mapped to `build_path`.
+ :param build_path: module's build root
+ :type build_path: str
+ :param sources_path: module's source root
+ :type sources_path: str
+ """
+ opts = self.get_or_create_compiler_options()
+
+ sources_path_rel = lambda x: os.path.normpath(os.path.join(sources_path, x))
+ build_path_rel = lambda x: os.path.normpath(os.path.join(build_path, x))
+
+ root_dir = opts["rootDir"]
+ out_dir = opts["outDir"]
+
+ opts["rootDir"] = sources_path_rel(root_dir)
+ opts["outDir"] = build_path_rel(out_dir)
+
+ if opts.get("typeRoots"):
+ opts["typeRoots"] = list(map(sources_path_rel, opts["typeRoots"])) + list(map(build_path_rel, opts["typeRoots"]))
+
+ if opts.get("paths") is None:
+ opts["paths"] = {}
+
+ # See: https://st.yandex-team.ru/FBP-47#62b4750775525b18f08205c7
+ opts["paths"]["*"] = ["*", "./@types/*"]
+
+ opts["baseUrl"] = "./node_modules"
+
+ self.data["include"] = list(map(sources_path_rel, self.data.get("include", [])))
+ self.data["exclude"] = list(map(sources_path_rel, self.data.get("exclude", [])))
+
+ if opts.get("sourceMap"):
+ opts["sourceRoot"] = os.path.relpath(root_dir, out_dir)
+
+ def write(self, path=None):
+ """
+ :param path: tsconfig path, defaults to original path
+ :type path: str
+ """
+ if path is None:
+ path = self.path
+
+ with open(path, "w") as f:
+ json.dump(self.data, f)
diff --git a/build/plugins/lib/nots/typescript/ts_errors.py b/build/plugins/lib/nots/typescript/ts_errors.py
new file mode 100644
index 0000000000..61c6c5dc05
--- /dev/null
+++ b/build/plugins/lib/nots/typescript/ts_errors.py
@@ -0,0 +1,19 @@
+class TsError(RuntimeError):
+ pass
+
+
+class TsValidationError(TsError):
+ def __init__(self, path, errors):
+ self.path = path
+ self.errors = errors
+
+ super(TsValidationError, self).__init__("Invalid tsconfig {}:\n{}".format(path, "\n".join(errors)))
+
+
+class TsCompilationError(TsError):
+ def __init__(self, code, stdout, stderr):
+ self.code = code
+ self.stdout = stdout
+ self.stderr = stderr
+
+ super(TsCompilationError, self).__init__("tsc exited with code {}:\n{}\n{}".format(code, stdout, stderr))
diff --git a/build/plugins/lib/nots/typescript/tsc_wrapper.py b/build/plugins/lib/nots/typescript/tsc_wrapper.py
index 9fddf6707f..2b54db7c8e 100644
--- a/build/plugins/lib/nots/typescript/tsc_wrapper.py
+++ b/build/plugins/lib/nots/typescript/tsc_wrapper.py
@@ -1,162 +1,11 @@
import os
-import json
import shutil
import subprocess
import tarfile
from ..package_manager import constants
-
-
-class TsError(RuntimeError):
- pass
-
-
-class TsValidationError(TsError):
- def __init__(self, path, errors):
- self.path = path
- self.errors = errors
-
- super(TsValidationError, self).__init__("Invalid tsconfig {}:\n{}".format(path, "\n".join(errors)))
-
-
-class TsCompilationError(TsError):
- def __init__(self, code, stdout, stderr):
- self.code = code
- self.stdout = stdout
- self.stderr = stderr
-
- super(TsCompilationError, self).__init__("tsc exited with code {}:\n{}\n{}".format(code, stdout, stderr))
-
-
-class TsConfig(object):
- @classmethod
- def load(cls, path):
- """
- :param path: tsconfig.json path
- :type path: str
- :rtype: TsConfig
- """
- tsconfig = cls(path)
- tsconfig.read()
-
- return tsconfig
-
- def __init__(self, path):
- if not os.path.isabs(path):
- raise TypeError("Absolute path required, given: {}".format(path))
-
- self.path = path
- self.data = {}
-
- def read(self):
- try:
- with open(self.path) as f:
- self.data = json.load(f)
- except Exception as e:
- raise TsError("Failed to read tsconfig {}: {}".format(self.path, e))
-
- def get_or_create_compiler_options(self):
- """
- Returns ref to the "compilerOptions" dict.
- :rtype: dict
- """
- opts = self.data.get("compilerOptions")
- if opts is None:
- opts = {}
- self.data["compilerOptions"] = opts
-
- return opts
-
- def compiler_option(self, name, default=None):
- """
- :param name: option key
- :type name: str
- :param default: default value
- :type default: mixed
- :rtype: mixed
- """
- return self.get_or_create_compiler_options().get(name, default)
-
- def validate(self):
- """
- Checks whether the config is compatible with current toolchain.
- """
- opts = self.get_or_create_compiler_options()
- errors = []
- root_dir = opts.get("rootDir")
- out_dir = opts.get("outDir")
- config_dir = os.path.dirname(self.path)
- is_mod_subdir = lambda p: not os.path.isabs(p) and os.path.normpath(os.path.join(config_dir, p)).startswith(config_dir)
-
- if root_dir is None:
- errors.append("'rootDir' option is required")
- elif not is_mod_subdir(root_dir):
- errors.append("'rootDir' should be a subdirectory of the module")
-
- if out_dir is None:
- errors.append("'outDir' option is required")
- elif not is_mod_subdir(out_dir):
- errors.append("'outDir' should be a subdirectory of the module")
-
- if opts.get("outFile") is not None:
- errors.append("'outFile' option is not supported")
-
- if opts.get("preserveSymlinks"):
- errors.append("'preserveSymlinks' option is not supported due to pnpm limitations")
-
- if opts.get("rootDirs") is not None:
- errors.append("'rootDirs' option is not supported, relative imports should have single root")
-
- if self.data.get("files") is not None:
- errors.append("'files' option is not supported, use 'include'")
-
- if self.data.get("references") is not None:
- errors.append("composite builds are not supported, use peerdirs in ya.make instead of 'references' option")
-
- if len(errors):
- raise TsValidationError(self.path, errors)
-
- def transform_paths(self, build_path, sources_path):
- """
- Updates config with correct abs paths.
- All source files/dirs will be mapped to `sources_path`, output files/dirs will be mapped to `build_path`.
- :param build_path: module's build root
- :type build_path: str
- :param sources_path: module's source root
- :type sources_path: str
- """
- opts = self.get_or_create_compiler_options()
-
- sources_path_rel = lambda x: os.path.normpath(os.path.join(sources_path, x))
- build_path_rel = lambda x: os.path.normpath(os.path.join(build_path, x))
-
- root_dir = opts["rootDir"]
- out_dir = opts["outDir"]
-
- opts["rootDir"] = sources_path_rel(root_dir)
- opts["outDir"] = build_path_rel(out_dir)
-
- if opts.get("typeRoots"):
- opts["typeRoots"] = list(map(sources_path_rel, opts["typeRoots"])) + list(map(build_path_rel, opts["typeRoots"]))
-
- opts["baseUrl"] = build_path_rel("node_modules")
-
- self.data["include"] = list(map(sources_path_rel, self.data.get("include", [])))
- self.data["exclude"] = list(map(sources_path_rel, self.data.get("exclude", [])))
-
- if opts.get("sourceMap"):
- opts["sourceRoot"] = os.path.relpath(root_dir, out_dir)
-
- def write(self, path=None):
- """
- :param path: tsconfig path, defaults to original path
- :type path: str
- """
- if path is None:
- path = self.path
-
- with open(path, "w") as f:
- json.dump(self.data, f)
+from .ts_config import TsConfig
+from .ts_errors import TsCompilationError
class TscWrapper(object):