@webiny/build-tools 0.0.0-unstable.0d717d18dd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/bundling/admin/createBuildAdmin.js +18 -0
  4. package/bundling/admin/createRsbuildConfig.js +174 -0
  5. package/bundling/admin/createWatchAdmin.js +15 -0
  6. package/bundling/admin/index.js +3 -0
  7. package/bundling/function/createBuildFunction.js +18 -0
  8. package/bundling/function/createRsbuildConfig.js +126 -0
  9. package/bundling/function/createWatchFunction.js +15 -0
  10. package/bundling/function/index.js +3 -0
  11. package/bundling/importValidatorPlugin.js +93 -0
  12. package/bundling/printBuildStats.js +46 -0
  13. package/index.d.ts +69 -0
  14. package/index.js +3 -0
  15. package/package.json +82 -0
  16. package/packages/buildPackage/copyToDist.js +11 -0
  17. package/packages/buildPackage/rslibCompile.js +65 -0
  18. package/packages/buildPackage/tsAliasReplacer.js +125 -0
  19. package/packages/buildPackage/tsCompile.js +80 -0
  20. package/packages/buildPackage/typescript/getTscBinaryPath.js +47 -0
  21. package/packages/buildPackage/typescript/readTsConfig.js +8 -0
  22. package/packages/buildPackage/typescript/runTsc.js +15 -0
  23. package/packages/buildPackage/typescript/writeTempTsConfig.js +10 -0
  24. package/packages/buildPackage/validateEsmImports.js +91 -0
  25. package/packages/buildPackage.js +93 -0
  26. package/packages/createBuildPackage.js +7 -0
  27. package/packages/createWatchPackage.js +7 -0
  28. package/packages/index.js +4 -0
  29. package/packages/watchPackage.js +39 -0
  30. package/traverseLoaders.js +14 -0
  31. package/utils/PackageJson.backup.ts +45 -0
  32. package/utils/PackageJson.d.ts +33 -0
  33. package/utils/PackageJson.js +44 -0
  34. package/utils.js +33 -0
  35. package/workspaces/index.js +15 -0
  36. package/workspaces/linkWorkspaces.js +100 -0
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@webiny/build-tools",
3
+ "version": "0.0.0-unstable.0d717d18dd",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./index.js",
7
+ "./*": "./*"
8
+ },
9
+ "description": "A collection of utilities for Webiny project.",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/webiny/webiny-js.git"
13
+ },
14
+ "author": "Pavel Denisjuk <pavel@webiny.com>",
15
+ "contributors": [
16
+ "Sven Al Hamad <sven@webiny.com>",
17
+ "Adrian Smijulj <adrian@webiny.com>"
18
+ ],
19
+ "dependencies": {
20
+ "@rsbuild/core": "2.1.9",
21
+ "@rsbuild/plugin-react": "2.1.0",
22
+ "@rsbuild/plugin-sass": "2.0.1",
23
+ "@rsbuild/plugin-svgr": "2.0.5",
24
+ "@rsbuild/plugin-type-check": "1.6.0",
25
+ "@rsdoctor/rspack-plugin": "1.6.1",
26
+ "@rslib/core": "0.23.2",
27
+ "@rspack/core": "2.1.7",
28
+ "@swc/plugin-emotion": "14.15.0",
29
+ "@tailwindcss/postcss": "4.3.3",
30
+ "@webiny/stdlib": "0.0.15",
31
+ "chalk": "6.0.0",
32
+ "css-loader": "7.1.4",
33
+ "fast-glob": "3.3.3",
34
+ "find-up": "8.0.0",
35
+ "fs-extra": "11.4.0",
36
+ "load-json-file": "7.0.1",
37
+ "lodash": "4.18.1",
38
+ "postcss-loader": "8.2.1",
39
+ "raw-loader": "4.0.2",
40
+ "react-dom": "18.3.1",
41
+ "react-refresh": "0.18.0",
42
+ "rimraf": "6.1.3",
43
+ "sass": "1.102.0",
44
+ "sass-loader": "17.0.0",
45
+ "strip-json-comments": "5.0.3",
46
+ "style-loader": "4.0.0",
47
+ "ts-morph": "28.0.0",
48
+ "tsx": "4.23.1",
49
+ "typescript": "7.0.2",
50
+ "url-loader": "4.1.1",
51
+ "utf-8-validate": "6.0.6"
52
+ },
53
+ "license": "MIT",
54
+ "adio": {
55
+ "ignore": {
56
+ "src": [
57
+ "@webiny/admin-ui",
58
+ "@webiny/app",
59
+ "!!raw-loader!"
60
+ ],
61
+ "dependencies": [
62
+ "@swc/plugin-emotion",
63
+ "postcss-loader",
64
+ "raw-loader",
65
+ "react-refresh",
66
+ "style-loader",
67
+ "typescript",
68
+ "sass-loader",
69
+ "sass",
70
+ "bufferutil",
71
+ "utf-8-validate",
72
+ "css-loader",
73
+ "url-loader",
74
+ "@rsdoctor/rspack-plugin"
75
+ ]
76
+ }
77
+ },
78
+ "publishConfig": {
79
+ "access": "public"
80
+ },
81
+ "gitHead": "b8aec8a1be3f25c3b428b357fe1e352c7cbff9ae"
82
+ }
@@ -0,0 +1,11 @@
1
+ import fs from "fs";
2
+ import { join } from "path";
3
+
4
+ export const copyToDist = (path, { cwd, logs, outputDir }) => {
5
+ const from = join(cwd, path);
6
+ const to = join(outputDir || join(cwd, "dist"), path);
7
+ if (fs.existsSync(from)) {
8
+ fs.copyFileSync(from, to);
9
+ logs !== false && console.log(`Copied ${path}.`);
10
+ }
11
+ };
@@ -0,0 +1,65 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { dirname } from "path";
4
+ import glob from "fast-glob";
5
+
6
+ const COMPILE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
7
+ const SKIP_EXTENSIONS = [".d.ts"];
8
+
9
+ export const rslibCompile = async ({ cwd, outputDir }) => {
10
+ const distRoot = outputDir || path.join(cwd, "dist");
11
+
12
+ // Copy non-compilable files (assets, json, graphql, etc.) as-is.
13
+ const pattern = path.join(cwd, "src/**/*.*").replace(/\\/g, "/");
14
+ const allFiles = glob.sync(pattern, { onlyFiles: true, dot: true });
15
+
16
+ for (const file of allFiles) {
17
+ const shouldCompile = COMPILE_EXTENSIONS.some(ext => file.endsWith(ext));
18
+ const shouldSkip = SKIP_EXTENSIONS.some(ext => file.endsWith(ext));
19
+
20
+ if (!shouldCompile || shouldSkip) {
21
+ const destPath = file.replace(path.join(cwd, "src"), distRoot);
22
+ fs.mkdirSync(dirname(destPath), { recursive: true });
23
+ fs.copyFileSync(file, destPath);
24
+ }
25
+ }
26
+
27
+ // Must be a dynamic import. @rslib/core statically loads rspack's native
28
+ // binding. Workers that also run createBuildAdmin load a different rspack
29
+ // version via rsbuild, so two native binaries end up in the same process
30
+ // and cause a SIGSEGV. Deferring the import keeps each rspack isolated to
31
+ // the workers that actually need it.
32
+ const [{ createRslib }, { pluginSvgr }] = await Promise.all([
33
+ import("@rslib/core"),
34
+ import("@rsbuild/plugin-svgr")
35
+ ]);
36
+
37
+ const rslib = await createRslib({
38
+ cwd,
39
+ config: {
40
+ lib: [{ format: "esm", bundle: false }],
41
+ source: {
42
+ // SVGs included so rslib runs them through pluginSvgr and emits
43
+ // a .js React-component module alongside the copied .svg asset.
44
+ entry: ["./src/**/*.{ts,tsx,js,jsx,svg}"],
45
+ alias: { "~": "./src" }
46
+ },
47
+ output: {
48
+ target: "web",
49
+ distPath: { root: path.relative(cwd, distRoot) || "." },
50
+ cleanDistPath: false,
51
+ sourceMap: { js: "source-map" },
52
+ // mixedImport emits each SVG as a static asset for the default URL
53
+ // export. Without a hash, two files named the same (e.g. fullscreen.svg
54
+ // in different dirs) collide at static/svg/fullscreen.svg.
55
+ // loader-utils [ext] has no leading dot, so "[name].[hash][ext]" produces
56
+ // "foo.hashsvg". Use an explicit ".svg" suffix instead.
57
+ filename: { svg: "[name].[contenthash:8].svg" }
58
+ },
59
+ plugins: [pluginSvgr({ mixedImport: true, svgrOptions: { exportType: "named" } })]
60
+ }
61
+ });
62
+
63
+ const { close } = await rslib.build();
64
+ await close();
65
+ };
@@ -0,0 +1,125 @@
1
+ import { dirname, join, relative, resolve } from "node:path";
2
+ import fg from "fast-glob";
3
+ import fs from "node:fs";
4
+
5
+ /**
6
+ *
7
+ * @param distDir {String}
8
+ * @param cwd {String}
9
+ * @param debug {Boolean}
10
+ * @returns {Promise<void>}
11
+ */
12
+ export const replaceTscAliases = async ({ distDir, cwd, debug = false }) => {
13
+ const dtsFiles = await fg("**/*.d.ts", {
14
+ cwd: distDir,
15
+ absolute: true
16
+ });
17
+
18
+ // Build a map of absolute src paths → package names, by scanning sibling packages.
19
+ // This is used to rewrite cross-package src paths (e.g. "../../api/src") back to
20
+ // proper package names (e.g. "@webiny/api") in generated .d.ts files.
21
+ const srcToPackageName = buildSrcToPackageNameMap(cwd);
22
+
23
+ for (const dtsFile of dtsFiles) {
24
+ let content = fs.readFileSync(dtsFile, "utf8").toString();
25
+ let modified = false;
26
+
27
+ // Replace all imports/exports with ~/ alias
28
+ content = content.replace(
29
+ /(from\s+["'])~\/([^"']+)(["'])/g,
30
+ (_, prefix, importPath, suffix) => {
31
+ modified = true;
32
+ // Calculate relative path from current file to dist root
33
+ const fileDir = dirname(dtsFile).replace(/\\/g, "/");
34
+ const normalizedDistDir = distDir.replace(/\\/g, "/");
35
+ const relativePath = relative(fileDir, normalizedDistDir).replace(/\\/g, "/");
36
+ const finalPath = relativePath
37
+ ? `${relativePath}/${importPath}`
38
+ : `./${importPath}`;
39
+ // Normalize to always start with ./
40
+ const normalized = finalPath.startsWith(".") ? finalPath : `./${finalPath}`;
41
+ return `${prefix}${normalized}${suffix}`;
42
+ }
43
+ );
44
+
45
+ // Replace cross-package src paths (e.g. "../../api/src/types") with proper package
46
+ // names (e.g. "@webiny/api/types"). TypeScript inlines these relative paths when
47
+ // project references are used and dependencies have paths pointing to src/.
48
+ if (srcToPackageName.size > 0) {
49
+ content = content.replace(
50
+ /["'](\.\.[^"']*\/src(?:\/[^"']*)?)['"]/g,
51
+ (match, relPath) => {
52
+ const quote = match[0];
53
+ const absPath = resolve(dirname(dtsFile), relPath);
54
+ const packageName = resolveToPackageName(absPath, srcToPackageName);
55
+ if (packageName) {
56
+ modified = true;
57
+ return `${quote}${packageName}${quote}`;
58
+ }
59
+ return match;
60
+ }
61
+ );
62
+ }
63
+
64
+ if (modified) {
65
+ fs.writeFileSync(dtsFile, content, "utf8");
66
+ if (debug) {
67
+ console.log(`Resolved aliases in: ${relative(cwd, dtsFile)}`);
68
+ }
69
+ }
70
+ }
71
+ };
72
+
73
+ /**
74
+ * Scans sibling packages to build a map of absolute src directory paths → package names.
75
+ * @param {string} cwd - The current package directory.
76
+ * @returns {Map<string, string>}
77
+ */
78
+ function buildSrcToPackageNameMap(cwd) {
79
+ const map = new Map();
80
+ // Go up to the packages/ directory (cwd is packages/xxx, parent is packages/)
81
+ const packagesDir = resolve(cwd, "..");
82
+ if (!fs.existsSync(packagesDir)) {
83
+ return map;
84
+ }
85
+ let entries;
86
+ try {
87
+ entries = fs.readdirSync(packagesDir);
88
+ } catch {
89
+ return map;
90
+ }
91
+ for (const entry of entries) {
92
+ const pkgDir = join(packagesDir, entry);
93
+ const pkgJsonPath = join(pkgDir, "package.json");
94
+ if (!fs.existsSync(pkgJsonPath)) {
95
+ continue;
96
+ }
97
+ try {
98
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
99
+ if (pkgJson.name) {
100
+ const srcDir = join(pkgDir, "src");
101
+ map.set(srcDir, pkgJson.name);
102
+ }
103
+ } catch {
104
+ // ignore
105
+ }
106
+ }
107
+ return map;
108
+ }
109
+
110
+ /**
111
+ * Given an absolute path that points into a package's src directory, returns the
112
+ * corresponding package name + sub-path (e.g. "@webiny/api/types").
113
+ * @param {string} absPath - Absolute path resolved from the relative import.
114
+ * @param {Map<string, string>} srcToPackageName
115
+ * @returns {string|null}
116
+ */
117
+ function resolveToPackageName(absPath, srcToPackageName) {
118
+ for (const [srcDir, packageName] of srcToPackageName) {
119
+ if (absPath === srcDir || absPath.startsWith(srcDir + "/")) {
120
+ const subPath = absPath.slice(srcDir.length).replace(/^\//, "");
121
+ return subPath ? `${packageName}/${subPath}` : packageName;
122
+ }
123
+ }
124
+ return null;
125
+ }
@@ -0,0 +1,80 @@
1
+ import { join, resolve } from "path";
2
+ import fs from "node:fs";
3
+ import merge from "lodash/merge.js";
4
+ import { replaceTscAliases } from "./tsAliasReplacer.js";
5
+ import { getTscBinaryPath } from "./typescript/getTscBinaryPath.js";
6
+ import { readTsConfig } from "./typescript/readTsConfig.js";
7
+ import { runTsc } from "./typescript/runTsc.js";
8
+ import { writeTempTsConfig } from "./typescript/writeTempTsConfig.js";
9
+
10
+ export const tsCompile = async ({ cwd = "", overrides, debug, outputDir, checkOnly = false }) => {
11
+ const normalizedCwd = cwd || process.cwd();
12
+ const tscPath = getTscBinaryPath();
13
+
14
+ const originalConfigPath = join(normalizedCwd, "tsconfig.build.json");
15
+ let tsConfigPath = originalConfigPath;
16
+ let tempConfigPath = null;
17
+ let resolvedOutDir = null;
18
+
19
+ const hasOverrides = overrides?.tsConfig || outputDir;
20
+
21
+ if (hasOverrides) {
22
+ let config = readTsConfig(originalConfigPath);
23
+
24
+ if (overrides?.tsConfig) {
25
+ if (typeof overrides.tsConfig === "function") {
26
+ config = overrides.tsConfig(config);
27
+ } else {
28
+ merge(config, overrides.tsConfig);
29
+ }
30
+
31
+ if (debug) {
32
+ console.log(`"tsconfig.build.json" overridden. New config:`);
33
+ console.log(config);
34
+ }
35
+ }
36
+
37
+ if (outputDir) {
38
+ config.compilerOptions = config.compilerOptions || {};
39
+ config.compilerOptions.outDir = outputDir;
40
+ config.compilerOptions.declarationDir = outputDir;
41
+ }
42
+
43
+ resolvedOutDir = config.compilerOptions?.outDir;
44
+ tempConfigPath = writeTempTsConfig(normalizedCwd, config);
45
+ tsConfigPath = tempConfigPath;
46
+ }
47
+
48
+ try {
49
+ const args = ["-p", tsConfigPath];
50
+ args.push("--tsBuildInfoFile", join(normalizedCwd, "tsconfig.build.tsbuildinfo"));
51
+
52
+ if (checkOnly) {
53
+ args.push("--noEmit");
54
+ }
55
+
56
+ runTsc(tscPath, args, normalizedCwd);
57
+ } finally {
58
+ if (tempConfigPath) {
59
+ fs.rmSync(tempConfigPath, { force: true });
60
+ }
61
+ }
62
+
63
+ if (!checkOnly) {
64
+ const distDir =
65
+ outputDir || resolveDistDir(normalizedCwd, originalConfigPath, resolvedOutDir);
66
+ await replaceTscAliases({ distDir, cwd: normalizedCwd, debug });
67
+ }
68
+ };
69
+
70
+ function resolveDistDir(cwd, configPath, overriddenOutDir) {
71
+ if (overriddenOutDir) {
72
+ return resolve(cwd, overriddenOutDir);
73
+ }
74
+ const config = readTsConfig(configPath);
75
+ const outDir = config.compilerOptions?.outDir;
76
+ if (outDir) {
77
+ return resolve(cwd, outDir);
78
+ }
79
+ return join(cwd, "dist");
80
+ }
@@ -0,0 +1,47 @@
1
+ import { createRequire } from "node:module";
2
+ import { dirname, join } from "node:path";
3
+ import fs from "node:fs";
4
+
5
+ let cachedPath = null;
6
+
7
+ export function getTscBinaryPath() {
8
+ if (cachedPath) {
9
+ return cachedPath;
10
+ }
11
+
12
+ const require = createRequire(import.meta.url);
13
+ const tsPackageJsonPath = require.resolve("typescript/package.json");
14
+ const tsDir = dirname(tsPackageJsonPath);
15
+
16
+ const platformPackage = "@typescript/typescript-" + process.platform + "-" + process.arch;
17
+
18
+ let platformPkgPath;
19
+ try {
20
+ const platformRequire = createRequire(join(tsDir, "package.json"));
21
+ platformPkgPath = platformRequire.resolve(platformPackage + "/package.json");
22
+ } catch {
23
+ throw new Error(
24
+ "Unable to resolve " +
25
+ platformPackage +
26
+ ". " +
27
+ "Either your platform is unsupported, or you are missing the package on disk."
28
+ );
29
+ }
30
+
31
+ const libDir = join(dirname(platformPkgPath), "lib");
32
+
33
+ let exe = join(libDir, "tsc");
34
+ if (process.platform === "win32") {
35
+ exe += ".exe";
36
+ if (exe.length >= 248) {
37
+ exe = "\\\\?\\" + exe;
38
+ }
39
+ }
40
+
41
+ if (!fs.existsSync(exe)) {
42
+ throw new Error("TypeScript native binary not found: " + exe);
43
+ }
44
+
45
+ cachedPath = exe;
46
+ return cachedPath;
47
+ }
@@ -0,0 +1,8 @@
1
+ import fs from "node:fs";
2
+ import stripJsonComments from "strip-json-comments";
3
+
4
+ export function readTsConfig(configPath) {
5
+ const content = fs.readFileSync(configPath, "utf8");
6
+ const stripped = stripJsonComments(content, { trailingCommas: true });
7
+ return JSON.parse(stripped);
8
+ }
@@ -0,0 +1,15 @@
1
+ import { execFileSync } from "node:child_process";
2
+
3
+ export function runTsc(tscPath, args, cwd) {
4
+ try {
5
+ execFileSync(tscPath, args, {
6
+ cwd,
7
+ stdio: ["pipe", "pipe", "pipe"],
8
+ encoding: "utf8",
9
+ maxBuffer: 10 * 1024 * 1024
10
+ });
11
+ } catch (error) {
12
+ const output = [error.stdout, error.stderr].filter(Boolean).join("\n").trim();
13
+ throw { message: output || error.message || "TypeScript compilation failed." };
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ import { join } from "node:path";
2
+ import fs from "node:fs";
3
+ import { randomBytes } from "node:crypto";
4
+
5
+ export function writeTempTsConfig(cwd, config) {
6
+ const suffix = randomBytes(4).toString("hex");
7
+ const tempPath = join(cwd, `tsconfig.build.tmp-${suffix}.json`);
8
+ fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), "utf8");
9
+ return tempPath;
10
+ }
@@ -0,0 +1,91 @@
1
+ import fs from "fs";
2
+ import { join } from "path";
3
+ import glob from "fast-glob";
4
+ import { Project, SyntaxKind } from "ts-morph";
5
+ import chalk from "chalk";
6
+
7
+ export const validateEsmImports = async ({ cwd }) => {
8
+ console.log("Validating ESM imports...");
9
+
10
+ const pattern = join(cwd, "src/**/*.{js,ts,tsx}").replace(/\\/g, "/");
11
+ const files = glob.sync(pattern, {
12
+ absolute: true,
13
+ onlyFiles: true
14
+ });
15
+
16
+ const errors = [];
17
+ const project = new Project({
18
+ useInMemoryFileSystem: true,
19
+ skipFileDependencyResolution: true
20
+ });
21
+
22
+ for (const filePath of files) {
23
+ const content = await fs.promises.readFile(filePath, "utf8");
24
+ const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });
25
+
26
+ // Get all import declarations: import { x } from 'y'
27
+ const importDeclarations = sourceFile.getImportDeclarations();
28
+ for (const importDecl of importDeclarations) {
29
+ const spec = importDecl.getModuleSpecifierValue();
30
+ await validateImportSpec(spec, filePath, errors);
31
+ }
32
+
33
+ // Get all export declarations: export { x } from 'y'
34
+ const exportDeclarations = sourceFile.getExportDeclarations();
35
+ for (const exportDecl of exportDeclarations) {
36
+ const moduleSpecifier = exportDecl.getModuleSpecifier();
37
+ if (moduleSpecifier) {
38
+ const spec = moduleSpecifier.getLiteralValue();
39
+ await validateImportSpec(spec, filePath, errors);
40
+ }
41
+ }
42
+
43
+ // Get all dynamic imports: import('y')
44
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
45
+ for (const callExpr of callExpressions) {
46
+ const expression = callExpr.getExpression();
47
+ if (expression.getKind() === SyntaxKind.ImportKeyword) {
48
+ const args = callExpr.getArguments();
49
+ if (args.length > 0 && args[0].getKind() === SyntaxKind.StringLiteral) {
50
+ const spec = args[0].getLiteralValue();
51
+ await validateImportSpec(spec, filePath, errors);
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ if (errors.length > 0) {
58
+ const invalidFiles = [...new Set(errors.map(e => e.file))];
59
+ const errorMessage = [
60
+ chalk.red("ESM import validation failed."),
61
+ `Found ${errors.length} invalid import(s) in ${invalidFiles.length} file(s):`,
62
+ "",
63
+ ...errors.map(e => {
64
+ const relativePath = e.file.replace(cwd, "").replace(/^[\/\\]/, "");
65
+ return ` ${chalk.red("-")} "${chalk.red(e.spec)}" in "${chalk.red(relativePath)}"`;
66
+ })
67
+ ].join("\n");
68
+ throw new Error(errorMessage);
69
+ } else {
70
+ console.log("All ESM imports are valid.");
71
+ }
72
+ };
73
+
74
+ async function validateImportSpec(spec, file, errors) {
75
+ if (spec.startsWith("~") || spec.startsWith("./") || spec.startsWith("../")) {
76
+ // Extract the file extension from the import path
77
+ const lastDotIndex = spec.lastIndexOf(".");
78
+ const lastSlashIndex = Math.max(spec.lastIndexOf("/"), spec.lastIndexOf("\\"));
79
+ const hasExtension = lastDotIndex > lastSlashIndex;
80
+
81
+ // If there's no extension, it should have .js
82
+ if (!hasExtension) {
83
+ const relativePath = file.replace(process.cwd(), "").replace(/^[\/\\]/, "");
84
+ const errorMsg = chalk.red(
85
+ `- Missing .js extension in import "${spec}" in ${relativePath}`
86
+ );
87
+ console.error(errorMsg);
88
+ errors.push({ file, spec, reason: "missing extension" });
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,93 @@
1
+ import * as rimraf from "rimraf";
2
+ import { join } from "path";
3
+ import fs from "node:fs";
4
+ import { rslibCompile } from "./buildPackage/rslibCompile.js";
5
+ import { tsCompile } from "./buildPackage/tsCompile.js";
6
+ import { copyToDist } from "./buildPackage/copyToDist.js";
7
+ import { validateEsmImports } from "./buildPackage/validateEsmImports.js";
8
+
9
+ export default async options => {
10
+ const start = new Date();
11
+
12
+ if (!options.cwd) {
13
+ options.cwd = "";
14
+ }
15
+ const { cwd = "" } = options;
16
+
17
+ rimraf.sync(join(cwd, "*.tsbuildinfo"), { glob: true });
18
+
19
+ options.logs !== false && console.log("Building...");
20
+
21
+ // Make sure `overrides` is an object.
22
+ if (options.overrides && typeof options.overrides === "string") {
23
+ options.overrides = JSON.parse(options.overrides);
24
+ }
25
+
26
+ // Validate ESM imports before compiling
27
+ await validateEsmImports({ cwd, logs: options.logs });
28
+
29
+ const distDir = join(cwd, "dist");
30
+
31
+ if (options.safeReplace) {
32
+ await buildWithSafeReplace(options, distDir);
33
+ } else {
34
+ await buildDirect(options, distDir);
35
+ }
36
+
37
+ const duration = (new Date() - start) / 1000;
38
+ options.logs !== false && console.log(`Done! Build finished in ${duration + "s"}.`);
39
+
40
+ return { duration };
41
+ };
42
+
43
+ async function buildDirect(options, distDir) {
44
+ // Clear dist/ contents without removing the directory itself.
45
+ // node_modules/@webiny/<pkg> symlinks directly to dist/, so deleting
46
+ // the directory would leave a dangling symlink and break Rspack resolution.
47
+ if (fs.existsSync(distDir)) {
48
+ for (const entry of fs.readdirSync(distDir)) {
49
+ fs.rmSync(join(distDir, entry), { recursive: true, force: true });
50
+ }
51
+ }
52
+
53
+ await rslibCompile(options);
54
+ await tsCompile(options);
55
+
56
+ options.logs !== false && console.log("Copying meta files...");
57
+ copyToDist("package.json", options);
58
+ copyToDist("LICENSE", options);
59
+ }
60
+
61
+ async function buildWithSafeReplace(options, distDir) {
62
+ const stagingDir = distDir + "._build";
63
+
64
+ // Clean up any leftover staging dir from a previous failed build.
65
+ if (fs.existsSync(stagingDir)) {
66
+ fs.rmSync(stagingDir, { recursive: true, force: true });
67
+ }
68
+ fs.mkdirSync(stagingDir, { recursive: true });
69
+
70
+ const stagingOptions = { ...options, outputDir: stagingDir };
71
+
72
+ await rslibCompile(stagingOptions);
73
+ await tsCompile(stagingOptions);
74
+
75
+ stagingOptions.logs !== false && console.log("Copying meta files...");
76
+ copyToDist("package.json", stagingOptions);
77
+ copyToDist("LICENSE", stagingOptions);
78
+
79
+ // Fast-swap: clear dist contents and move staged files in.
80
+ if (fs.existsSync(distDir)) {
81
+ for (const entry of fs.readdirSync(distDir)) {
82
+ fs.rmSync(join(distDir, entry), { recursive: true, force: true });
83
+ }
84
+ } else {
85
+ fs.mkdirSync(distDir, { recursive: true });
86
+ }
87
+
88
+ for (const entry of fs.readdirSync(stagingDir)) {
89
+ fs.renameSync(join(stagingDir, entry), join(distDir, entry));
90
+ }
91
+
92
+ fs.rmSync(stagingDir, { recursive: true, force: true });
93
+ }
@@ -0,0 +1,7 @@
1
+ import buildPackage from "./buildPackage.js";
2
+ import { prepareOptions } from "../utils.js";
3
+
4
+ export default config => async options => {
5
+ const preparedOptions = prepareOptions({ config, options });
6
+ return buildPackage(preparedOptions);
7
+ };
@@ -0,0 +1,7 @@
1
+ import watchPackage from "./watchPackage.js";
2
+ import { prepareOptions } from "../utils.js";
3
+
4
+ export default config => async options => {
5
+ const preparedOptions = prepareOptions({ config, options });
6
+ return watchPackage(preparedOptions);
7
+ };
@@ -0,0 +1,4 @@
1
+ export { default as watchPackage } from "./watchPackage.js";
2
+ export { default as createWatchPackage } from "./createWatchPackage.js";
3
+ export { default as buildPackage } from "./buildPackage.js";
4
+ export { default as createBuildPackage } from "./createBuildPackage.js";