@webiny/build-tools 0.0.0-unstable.3c5210ad37
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.
- package/.babelrc.js +1 -0
- package/.eslintrc.cjs +6 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/bundling/admin/createBuildAdmin.js +17 -0
- package/bundling/admin/createRsbuildConfig.js +142 -0
- package/bundling/admin/createWatchAdmin.js +14 -0
- package/bundling/admin/index.js +3 -0
- package/bundling/function/createBuildFunction.js +17 -0
- package/bundling/function/createRsbuildConfig.js +84 -0
- package/bundling/function/createWatchFunction.js +14 -0
- package/bundling/function/index.js +3 -0
- package/bundling/importValidatorPlugin.js +82 -0
- package/bundling/printBuildStats.js +44 -0
- package/index.d.ts +82 -0
- package/index.js +8 -0
- package/package.json +93 -0
- package/packages/buildPackage/babelCompile.js +103 -0
- package/packages/buildPackage/copyToDist.js +11 -0
- package/packages/buildPackage/tsAliasReplacer.js +39 -0
- package/packages/buildPackage/tsCompile.js +69 -0
- package/packages/buildPackage/validateEsmImports.js +91 -0
- package/packages/buildPackage.js +40 -0
- package/packages/createBabelConfigForNode.js +19 -0
- package/packages/createBabelConfigForReact.js +20 -0
- package/packages/createBuildPackage.js +7 -0
- package/packages/createSwcConfigForNode.js +45 -0
- package/packages/createWatchPackage.js +7 -0
- package/packages/index.js +6 -0
- package/packages/watchPackage.js +117 -0
- package/traverseLoaders.js +14 -0
- package/utils/PackageJson.backup.ts +46 -0
- package/utils/PackageJson.d.ts +33 -0
- package/utils/PackageJson.js +44 -0
- package/utils.js +33 -0
- package/workspaces/index.js +12 -0
- package/workspaces/linkWorkspaces.js +107 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { dirname, extname, join, parse, relative } from "path";
|
|
3
|
+
import * as babel from "@babel/core";
|
|
4
|
+
import glob from "fast-glob";
|
|
5
|
+
|
|
6
|
+
const BABEL_COMPILE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
|
|
7
|
+
const BABEL_SKIP_EXTENSIONS = [".d.ts"];
|
|
8
|
+
|
|
9
|
+
const withSourceMapUrl = (file, code) => {
|
|
10
|
+
const { name } = parse(file);
|
|
11
|
+
return [code, "", `//# sourceMappingURL=${name}.js.map`].join("\n");
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getDistCopyFilePath = ({ file, cwd }) => {
|
|
15
|
+
const relativeDir = relative(cwd, file);
|
|
16
|
+
return join(cwd, relativeDir.replace("src", "dist"));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getDistFilePaths = ({ file, cwd }) => {
|
|
20
|
+
const { dir, name } = parse(file);
|
|
21
|
+
|
|
22
|
+
const relativeDir = relative(cwd, dir);
|
|
23
|
+
|
|
24
|
+
const code = join(cwd, relativeDir.replace("src", "dist"), name + ".js");
|
|
25
|
+
const map = join(cwd, relativeDir.replace("src", "dist"), name + ".js.map");
|
|
26
|
+
return { code, map };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const babelCompile = async ({ cwd }) => {
|
|
30
|
+
// We're passing "*.*" just because we want to copy all files that cannot be compiled.
|
|
31
|
+
// We want to have the same behaviour that the Babel CLI's "--copy-files" flag provides.
|
|
32
|
+
const pattern = join(cwd, "src/**/*.*").replace(/\\/g, "/");
|
|
33
|
+
const files = glob.sync(pattern, {
|
|
34
|
+
onlyFiles: true,
|
|
35
|
+
dot: true
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const compilations = [];
|
|
39
|
+
const copies = [];
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < files.length; i++) {
|
|
42
|
+
const file = files[i];
|
|
43
|
+
if (
|
|
44
|
+
fileEndsWith(file, BABEL_COMPILE_EXTENSIONS) &&
|
|
45
|
+
!fileEndsWith(file, BABEL_SKIP_EXTENSIONS)
|
|
46
|
+
) {
|
|
47
|
+
compilations.push(
|
|
48
|
+
babel
|
|
49
|
+
.transformFileAsync(file, {
|
|
50
|
+
cwd,
|
|
51
|
+
sourceMaps: true
|
|
52
|
+
})
|
|
53
|
+
.then(results => [file, results])
|
|
54
|
+
);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// All other files should be copied as-is.
|
|
59
|
+
copies.push(copyFile(file, cwd));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// At this point, just wait for compilations to be completed, so we can proceed with writing the files ASAP.
|
|
63
|
+
await Promise.all(compilations);
|
|
64
|
+
|
|
65
|
+
const writes = [];
|
|
66
|
+
for (let i = 0; i < compilations.length; i++) {
|
|
67
|
+
const [file, result] = await compilations[i];
|
|
68
|
+
const { code, map } = result;
|
|
69
|
+
|
|
70
|
+
const paths = getDistFilePaths({ file, cwd });
|
|
71
|
+
fs.mkdirSync(dirname(paths.code), { recursive: true });
|
|
72
|
+
|
|
73
|
+
// Save the compiled JS file.
|
|
74
|
+
writes.push(fs.promises.writeFile(paths.code, withSourceMapUrl(file, code), "utf8"));
|
|
75
|
+
|
|
76
|
+
// Save source maps file.
|
|
77
|
+
const mapJson = JSON.stringify(map);
|
|
78
|
+
writes.push(fs.promises.writeFile(paths.map, mapJson, "utf8"));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Wait until all files have been written to disk.
|
|
82
|
+
return Promise.all([...writes, ...copies]);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function copyFile(file, cwd) {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
try {
|
|
88
|
+
const destPath = getDistCopyFilePath({ file, cwd });
|
|
89
|
+
if (!fs.existsSync(dirname(destPath))) {
|
|
90
|
+
fs.mkdirSync(dirname(destPath), { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fs.copyFileSync(file, destPath);
|
|
94
|
+
resolve();
|
|
95
|
+
} catch (e) {
|
|
96
|
+
reject(e);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function fileEndsWith(file, extensions) {
|
|
102
|
+
return extensions.some(ext => file.endsWith(ext));
|
|
103
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
export const copyToDist = (path, { cwd, logs }) => {
|
|
5
|
+
const from = join(cwd, path);
|
|
6
|
+
const to = 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,39 @@
|
|
|
1
|
+
import { dirname, relative } from "path";
|
|
2
|
+
import fg from "fast-glob";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
export const replaceTscAliases = async ({ distDir, cwd, debug = false }) => {
|
|
6
|
+
const dtsFiles = await fg("**/*.d.ts", {
|
|
7
|
+
cwd: distDir,
|
|
8
|
+
absolute: true
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
for (const dtsFile of dtsFiles) {
|
|
12
|
+
let content = fs.readFileSync(dtsFile, "utf8");
|
|
13
|
+
let modified = false;
|
|
14
|
+
|
|
15
|
+
// Replace all imports/exports with ~/ alias
|
|
16
|
+
content = content.replace(
|
|
17
|
+
/(from\s+["'])~\/([^"']+)(["'])/g,
|
|
18
|
+
(match, prefix, importPath, suffix) => {
|
|
19
|
+
modified = true;
|
|
20
|
+
// Calculate relative path from current file to dist root
|
|
21
|
+
const fileDir = dirname(dtsFile);
|
|
22
|
+
const relativePath = relative(fileDir, distDir);
|
|
23
|
+
const finalPath = relativePath
|
|
24
|
+
? `${relativePath}/${importPath}`
|
|
25
|
+
: `./${importPath}`;
|
|
26
|
+
// Normalize to always start with ./
|
|
27
|
+
const normalized = finalPath.startsWith(".") ? finalPath : `./${finalPath}`;
|
|
28
|
+
return `${prefix}${normalized}${suffix}`;
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (modified) {
|
|
33
|
+
fs.writeFileSync(dtsFile, content, "utf8");
|
|
34
|
+
if (debug) {
|
|
35
|
+
console.log(`Resolved ~ aliases in: ${relative(cwd, dtsFile)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import merge from "lodash/merge.js";
|
|
4
|
+
import { replaceTscAliases } from "./tsAliasReplacer.js";
|
|
5
|
+
|
|
6
|
+
export const tsCompile = async ({ cwd = "", overrides, debug }) => {
|
|
7
|
+
const tsConfigPath = join(cwd, "tsconfig.build.json");
|
|
8
|
+
|
|
9
|
+
let { config: readTsConfig } = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
|
|
10
|
+
|
|
11
|
+
if (overrides.tsConfig) {
|
|
12
|
+
if (typeof overrides.tsConfig === "function") {
|
|
13
|
+
readTsConfig = overrides.tsConfig(readTsConfig);
|
|
14
|
+
} else {
|
|
15
|
+
merge(readTsConfig, overrides.tsConfig);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (debug) {
|
|
19
|
+
console.log(`"tsconfig.build.json" overridden. New config:`);
|
|
20
|
+
console.log(readTsConfig);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const parsedJsonConfigFile = ts.parseJsonConfigFileContent(readTsConfig, ts.sys, cwd);
|
|
24
|
+
|
|
25
|
+
const { projectReferences, options, fileNames, errors } = parsedJsonConfigFile;
|
|
26
|
+
|
|
27
|
+
// Exclude .d.ts files from TypeScript compilation
|
|
28
|
+
const filteredFileNames = fileNames.filter(fileName => !fileName.endsWith(".d.ts"));
|
|
29
|
+
|
|
30
|
+
const program = ts.createProgram({
|
|
31
|
+
projectReferences,
|
|
32
|
+
options,
|
|
33
|
+
rootNames: filteredFileNames,
|
|
34
|
+
configFileParsingDiagnostics: errors
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const { diagnostics, emitSkipped } = program.emit(
|
|
38
|
+
undefined, // targetSourceFile
|
|
39
|
+
(fileName, data, writeByteOrderMark, onError, sourceFiles) => {
|
|
40
|
+
// Only emit files within the current package directory
|
|
41
|
+
const relativePath = fileName.replace(cwd, "");
|
|
42
|
+
if (fileName.startsWith(cwd) && !relativePath.includes("../")) {
|
|
43
|
+
ts.sys.writeFile(fileName, data, writeByteOrderMark);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(diagnostics, errors);
|
|
49
|
+
|
|
50
|
+
if (allDiagnostics.length) {
|
|
51
|
+
const formatHost = {
|
|
52
|
+
getCanonicalFileName: path => path,
|
|
53
|
+
getCurrentDirectory: () => cwd,
|
|
54
|
+
getNewLine: () => ts.sys.newLine
|
|
55
|
+
};
|
|
56
|
+
const message = ts.formatDiagnostics(allDiagnostics, formatHost);
|
|
57
|
+
if (message) {
|
|
58
|
+
throw { message };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (emitSkipped) {
|
|
63
|
+
throw { message: "TypeScript compilation failed." };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Resolve ~ path aliases in .d.ts files
|
|
67
|
+
const distDir = options.outDir || join(cwd, "dist");
|
|
68
|
+
await replaceTscAliases({ distDir, cwd, debug });
|
|
69
|
+
};
|
|
@@ -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,40 @@
|
|
|
1
|
+
import * as rimraf from "rimraf";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { babelCompile } from "./buildPackage/babelCompile.js";
|
|
4
|
+
import { tsCompile } from "./buildPackage/tsCompile.js";
|
|
5
|
+
import { copyToDist } from "./buildPackage/copyToDist.js";
|
|
6
|
+
import { validateEsmImports } from "./buildPackage/validateEsmImports.js";
|
|
7
|
+
|
|
8
|
+
export default async options => {
|
|
9
|
+
const start = new Date();
|
|
10
|
+
|
|
11
|
+
if (!options.cwd) {
|
|
12
|
+
options.cwd = "";
|
|
13
|
+
}
|
|
14
|
+
const { cwd = "" } = options;
|
|
15
|
+
options.logs !== false && console.log("Deleting existing build files...");
|
|
16
|
+
rimraf.sync(join(cwd, "./dist"));
|
|
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
|
+
await babelCompile(options);
|
|
30
|
+
await tsCompile(options);
|
|
31
|
+
|
|
32
|
+
options.logs !== false && console.log("Copying meta files...");
|
|
33
|
+
copyToDist("package.json", options);
|
|
34
|
+
copyToDist("LICENSE", options);
|
|
35
|
+
|
|
36
|
+
const duration = (new Date() - start) / 1000;
|
|
37
|
+
options.logs !== false && console.log(`Done! Build finished in ${duration + "s"}.`);
|
|
38
|
+
|
|
39
|
+
return { duration };
|
|
40
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default ({ path }) => {
|
|
2
|
+
return {
|
|
3
|
+
presets: [
|
|
4
|
+
["@babel/preset-react", { useBuiltIns: true }],
|
|
5
|
+
["@babel/preset-typescript", { isTSX: true, allExtensions: true }]
|
|
6
|
+
],
|
|
7
|
+
plugins: [
|
|
8
|
+
[
|
|
9
|
+
"babel-plugin-module-resolver",
|
|
10
|
+
{
|
|
11
|
+
cwd: path,
|
|
12
|
+
alias: {
|
|
13
|
+
"~": "./src"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
]
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default ({ path }) => ({
|
|
2
|
+
presets: [
|
|
3
|
+
["@babel/preset-react", { useBuiltIns: true }],
|
|
4
|
+
["@babel/preset-typescript", { isTSX: true, allExtensions: true }]
|
|
5
|
+
],
|
|
6
|
+
plugins: [
|
|
7
|
+
"babel-plugin-macros",
|
|
8
|
+
"@babel/plugin-proposal-throw-expressions",
|
|
9
|
+
["@emotion/babel-plugin", { autoLabel: "dev-only" }],
|
|
10
|
+
[
|
|
11
|
+
"babel-plugin-module-resolver",
|
|
12
|
+
{
|
|
13
|
+
cwd: path,
|
|
14
|
+
alias: {
|
|
15
|
+
"~": "./src"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
]
|
|
20
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import readJsonSync from "read-json-sync";
|
|
2
|
+
|
|
3
|
+
export default ({ path, esm }) => {
|
|
4
|
+
return {
|
|
5
|
+
presets: [
|
|
6
|
+
[
|
|
7
|
+
"@babel/preset-env",
|
|
8
|
+
{
|
|
9
|
+
targets: {
|
|
10
|
+
node: "18"
|
|
11
|
+
},
|
|
12
|
+
modules: esm ? false : "auto",
|
|
13
|
+
exclude: [
|
|
14
|
+
"transform-typeof-symbol",
|
|
15
|
+
"@babel/plugin-proposal-optional-chaining",
|
|
16
|
+
"@babel/plugin-proposal-nullish-coalescing-operator",
|
|
17
|
+
"@babel/plugin-proposal-class-properties",
|
|
18
|
+
"@babel/plugin-transform-async-to-generator",
|
|
19
|
+
"@babel/plugin-transform-regenerator",
|
|
20
|
+
"@babel/plugin-proposal-dynamic-import"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"@babel/preset-typescript"
|
|
25
|
+
],
|
|
26
|
+
plugins: [
|
|
27
|
+
[
|
|
28
|
+
"@babel/plugin-transform-runtime",
|
|
29
|
+
{
|
|
30
|
+
useESModules: !!esm,
|
|
31
|
+
version: readJsonSync(require.resolve("@babel/runtime/package.json")).version
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
"babel-plugin-module-resolver",
|
|
36
|
+
{
|
|
37
|
+
cwd: path,
|
|
38
|
+
alias: {
|
|
39
|
+
"~": "./src"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
]
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
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";
|
|
5
|
+
export { default as createBabelConfigForNode } from "./createBabelConfigForNode.js";
|
|
6
|
+
export { default as createBabelConfigForReact } from "./createBabelConfigForReact.js";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import { transformFileAsync } from "@babel/core";
|
|
4
|
+
import chokidar from "chokidar";
|
|
5
|
+
import baseFs from "node:fs";
|
|
6
|
+
|
|
7
|
+
let compilationQueue = [];
|
|
8
|
+
let debounceTimer = null;
|
|
9
|
+
const DEBOUNCE_DELAY = 1000; // 1 second
|
|
10
|
+
|
|
11
|
+
const flushCompilationQueue = () => {
|
|
12
|
+
if (compilationQueue.length > 0) {
|
|
13
|
+
if (compilationQueue.length === 1) {
|
|
14
|
+
console.log(`Successfully compiled ${compilationQueue[0]}.`);
|
|
15
|
+
} else {
|
|
16
|
+
console.log(`Successfully compiled ${compilationQueue.length} files.`);
|
|
17
|
+
}
|
|
18
|
+
compilationQueue = [];
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const logCompilation = inputPathRelative => {
|
|
23
|
+
compilationQueue.push(inputPathRelative);
|
|
24
|
+
|
|
25
|
+
clearTimeout(debounceTimer);
|
|
26
|
+
debounceTimer = setTimeout(() => {
|
|
27
|
+
flushCompilationQueue();
|
|
28
|
+
}, DEBOUNCE_DELAY);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getBabelFile = cwd => {
|
|
32
|
+
const babelJs = path.join(cwd, ".babelrc.js");
|
|
33
|
+
const babelCjs = path.join(cwd, ".babelrc.cjs");
|
|
34
|
+
|
|
35
|
+
if (baseFs.existsSync(babelJs)) {
|
|
36
|
+
return babelJs;
|
|
37
|
+
} else if (baseFs.existsSync(babelCjs)) {
|
|
38
|
+
return babelCjs;
|
|
39
|
+
}
|
|
40
|
+
throw new Error("No Babel configuration file found (.babelrc.js or .babelrc.cjs).");
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const compileFile = async (cwd, inputPath, outputPath) => {
|
|
44
|
+
const inputPathRelative = path.relative(cwd, inputPath);
|
|
45
|
+
|
|
46
|
+
const configFile = getBabelFile(cwd);
|
|
47
|
+
|
|
48
|
+
const result = await transformFileAsync(inputPath, {
|
|
49
|
+
configFile: configFile,
|
|
50
|
+
sourceMaps: true
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!result || !result.code) {
|
|
54
|
+
throw new Error(`Failed to compile: ${inputPath}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Write compiled file
|
|
58
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
59
|
+
await fs.writeFile(outputPath, result.code);
|
|
60
|
+
|
|
61
|
+
// Write source map
|
|
62
|
+
if (result.map) {
|
|
63
|
+
await fs.writeFile(`${outputPath}.map`, JSON.stringify(result.map));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
logCompilation(inputPathRelative);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const srcToDist = filePath =>
|
|
70
|
+
path.join(
|
|
71
|
+
filePath
|
|
72
|
+
.replace(`${path.sep}src${path.sep}`, `${path.sep}dist${path.sep}`)
|
|
73
|
+
.replace(/\.(ts|tsx)$/, ".js")
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export default async options => {
|
|
77
|
+
const srcDir = path.join(options.cwd, "src");
|
|
78
|
+
|
|
79
|
+
// Watch the src directory recursively for new files
|
|
80
|
+
const watcher = chokidar.watch(srcDir, {
|
|
81
|
+
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
|
82
|
+
persistent: true,
|
|
83
|
+
ignoreInitial: false
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const isTsFile = filePath => /\.(ts|tsx)$/.test(filePath);
|
|
87
|
+
|
|
88
|
+
watcher.on("add", async srcPath => {
|
|
89
|
+
if (!isTsFile(srcPath)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const distPath = srcToDist(srcPath);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
await compileFile(options.cwd, srcPath, distPath);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("Error compiling:", err);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
watcher.on("change", async srcPath => {
|
|
103
|
+
if (!isTsFile(srcPath)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const distPath = srcToDist(srcPath);
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await compileFile(options.cwd, srcPath, distPath);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error("Error compiling:", err);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log("Watching for changes...");
|
|
117
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility to recursively traverse loaders and execute the "onLoader" callback.
|
|
3
|
+
*/
|
|
4
|
+
export const traverseLoaders = (loaders, onLoader) => {
|
|
5
|
+
for (const loader of loaders) {
|
|
6
|
+
if (loader.oneOf) {
|
|
7
|
+
traverseLoaders(loader.oneOf, onLoader);
|
|
8
|
+
} else if (loader.use) {
|
|
9
|
+
traverseLoaders(loader.use, onLoader);
|
|
10
|
+
} else {
|
|
11
|
+
onLoader(loader);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// We'll use this class once the package is converted to TS!
|
|
2
|
+
|
|
3
|
+
import readJson from "read-json-sync";
|
|
4
|
+
import findUp from "find-up";
|
|
5
|
+
|
|
6
|
+
export class PackageJson {
|
|
7
|
+
private readonly filePath: string;
|
|
8
|
+
private readonly json: Record<string, any>;
|
|
9
|
+
|
|
10
|
+
static fromFile(filePath: string) {
|
|
11
|
+
return new PackageJson(filePath, readJson(filePath));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static async findClosest(fromPath: string) {
|
|
15
|
+
const closestPackageJson = await findUp("package.json", { cwd: fromPath });
|
|
16
|
+
if (!closestPackageJson) {
|
|
17
|
+
throw Error(`Failed to find ${fromPath}`);
|
|
18
|
+
}
|
|
19
|
+
return PackageJson.fromFile(closestPackageJson);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static async fromPackage(packageName: string, cwd?: string) {
|
|
23
|
+
const jsonPath = await findUp(`node_modules/${packageName}/package.json`, {
|
|
24
|
+
cwd: cwd ?? process.cwd()
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!jsonPath) {
|
|
28
|
+
throw Error(`Failed to find package ${packageName}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return PackageJson.fromFile(jsonPath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private constructor(filePath: string, json: Record<string, any>) {
|
|
35
|
+
this.filePath = filePath;
|
|
36
|
+
this.json = json;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getLocation() {
|
|
40
|
+
return this.filePath;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getJson() {
|
|
44
|
+
return this.json;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare class PackageJson {
|
|
2
|
+
private readonly filePath: string;
|
|
3
|
+
private readonly json: Record<string, any>;
|
|
4
|
+
|
|
5
|
+
private constructor(filePath: string, json: Record<string, any>);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load a PackageJson instance from a given file path.
|
|
9
|
+
*/
|
|
10
|
+
static fromFile(filePath: string): PackageJson;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Find the closest package.json starting from the given path.
|
|
14
|
+
* Throws if no package.json is found.
|
|
15
|
+
*/
|
|
16
|
+
static findClosest(fromPath: string): Promise<PackageJson>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load a PackageJson instance from a package in node_modules.
|
|
20
|
+
* Throws if the package.json cannot be found.
|
|
21
|
+
*/
|
|
22
|
+
static fromPackage(packageName: string, cwd?: string): Promise<PackageJson>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the absolute path to this package.json file.
|
|
26
|
+
*/
|
|
27
|
+
getLocation(): string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the raw JSON contents of the package.json file.
|
|
31
|
+
*/
|
|
32
|
+
getJson(): Record<string, any>;
|
|
33
|
+
}
|