@checkdigit/typescript-config 7.0.0-PR.54-eb75 → 7.0.0-PR.54-950f
Sign up to get free protection for your applications and to get access to all the features.
- package/dist-mjs/analyze.mjs +30 -0
- package/dist-mjs/builder.mjs +47 -0
- package/dist-mjs/compile.mjs +182 -0
- package/dist-mjs/index.mjs +10 -0
- package/dist-mjs/test/module-directory/index.mjs +6 -0
- package/dist-types/analyze.d.ts +6 -0
- package/dist-types/builder.d.ts +1 -0
- package/dist-types/compile.d.ts +81 -0
- package/dist-types/index.d.ts +4 -0
- package/dist-types/test/module-directory/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/analyze.ts +34 -0
- package/src/builder.ts +57 -0
- package/src/compile.ts +320 -0
- package/src/index.ts +7 -0
- package/src/test/module-directory/index.ts +3 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
// src/analyze.ts
|
2
|
+
import { strict as assert } from "node:assert";
|
3
|
+
function analyze(metafile) {
|
4
|
+
const source = new Set(Object.keys(metafile.inputs).filter((key) => !key.startsWith("node_modules")));
|
5
|
+
const modules = new Set(Object.keys(metafile.inputs).filter((key) => key.startsWith("node_modules")));
|
6
|
+
const [output] = Object.entries(metafile.outputs);
|
7
|
+
assert.ok(output !== void 0);
|
8
|
+
const [, bundle] = output;
|
9
|
+
const sourceBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
|
10
|
+
if (source.has(file)) {
|
11
|
+
return bytes + value.bytesInOutput;
|
12
|
+
}
|
13
|
+
return bytes;
|
14
|
+
}, 0);
|
15
|
+
const moduleBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
|
16
|
+
if (modules.has(file)) {
|
17
|
+
return bytes + value.bytesInOutput;
|
18
|
+
}
|
19
|
+
return bytes;
|
20
|
+
}, 0);
|
21
|
+
return {
|
22
|
+
sourceBytes,
|
23
|
+
moduleBytes,
|
24
|
+
totalBytes: bundle.bytes
|
25
|
+
};
|
26
|
+
}
|
27
|
+
export {
|
28
|
+
analyze as default
|
29
|
+
};
|
30
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2FuYWx5emUudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsU0FBUyxVQUFVLGNBQWM7QUFJbEIsU0FBUixRQUF5QixVQUFvQjtBQUNsRCxRQUFNLFNBQVMsSUFBSSxJQUFJLE9BQU8sS0FBSyxTQUFTLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksV0FBVyxjQUFjLENBQUMsQ0FBQztBQUNwRyxRQUFNLFVBQVUsSUFBSSxJQUFJLE9BQU8sS0FBSyxTQUFTLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxJQUFJLFdBQVcsY0FBYyxDQUFDLENBQUM7QUFFcEcsUUFBTSxDQUFDLE1BQU0sSUFBSSxPQUFPLFFBQVEsU0FBUyxPQUFPO0FBQ2hELFNBQU8sR0FBRyxXQUFXLE1BQVM7QUFDOUIsUUFBTSxDQUFDLEVBQUUsTUFBTSxJQUFJO0FBRW5CLFFBQU0sY0FBYyxPQUFPLFFBQVEsT0FBTyxNQUFNLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssTUFBTTtBQUNqRixRQUFJLE9BQU8sSUFBSSxJQUFJLEdBQUc7QUFDcEIsYUFBTyxRQUFRLE1BQU07QUFBQSxJQUN2QjtBQUNBLFdBQU87QUFBQSxFQUNULEdBQUcsQ0FBQztBQUVKLFFBQU0sY0FBYyxPQUFPLFFBQVEsT0FBTyxNQUFNLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssTUFBTTtBQUNqRixRQUFJLFFBQVEsSUFBSSxJQUFJLEdBQUc7QUFDckIsYUFBTyxRQUFRLE1BQU07QUFBQSxJQUN2QjtBQUNBLFdBQU87QUFBQSxFQUNULEdBQUcsQ0FBQztBQUVKLFNBQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLElBQ0EsWUFBWSxPQUFPO0FBQUEsRUFDckI7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// src/builder.ts
|
2
|
+
import { strict as assert } from "node:assert";
|
3
|
+
import { promises as fs } from "node:fs";
|
4
|
+
import path from "node:path";
|
5
|
+
import { parseArgs } from "node:util";
|
6
|
+
import { compile, analyze } from "./index.mjs";
|
7
|
+
var {
|
8
|
+
values: { type, inDir, outDir, entryPoint, outFile, external, minify, sourceMap }
|
9
|
+
} = parseArgs({
|
10
|
+
options: {
|
11
|
+
type: { type: "string", short: "t", default: "module" },
|
12
|
+
inDir: { type: "string", short: "i", default: "src" },
|
13
|
+
outDir: { type: "string", short: "o", default: "build" },
|
14
|
+
entryPoint: { type: "string", short: "e", default: void 0 },
|
15
|
+
outFile: { type: "string", short: "f", default: void 0 },
|
16
|
+
external: { type: "string", short: "x", multiple: true, default: [] },
|
17
|
+
minify: { type: "boolean", short: "m", default: false },
|
18
|
+
sourceMap: { type: "boolean", short: "s", default: false }
|
19
|
+
}
|
20
|
+
});
|
21
|
+
assert.ok(type === "module" || type === "types", "type must be types or module");
|
22
|
+
assert.ok(inDir !== void 0, "inDir is required");
|
23
|
+
assert.ok(outDir !== void 0, "outDir is required");
|
24
|
+
var compileResult = await compile({
|
25
|
+
type,
|
26
|
+
inDir: path.join(process.cwd(), inDir),
|
27
|
+
outDir: path.join(process.cwd(), outDir),
|
28
|
+
entryPoint,
|
29
|
+
outFile,
|
30
|
+
external,
|
31
|
+
minify,
|
32
|
+
sourceMap
|
33
|
+
});
|
34
|
+
await Promise.all(
|
35
|
+
compileResult.outputFiles.map(async (file) => {
|
36
|
+
await fs.mkdir(path.join(path.dirname(file.path)), { recursive: true });
|
37
|
+
await fs.writeFile(file.path, file.text);
|
38
|
+
})
|
39
|
+
);
|
40
|
+
if (compileResult.metafile !== void 0) {
|
41
|
+
const analysis = analyze(compileResult.metafile);
|
42
|
+
await fs.writeFile(path.join(outDir, "metafile.json"), JSON.stringify(compileResult.metafile, void 0, 2));
|
43
|
+
console.log(
|
44
|
+
`${outFile}: src ${analysis.sourceBytes}, node_modules ${analysis.moduleBytes}, total ${analysis.totalBytes}`
|
45
|
+
);
|
46
|
+
}
|
47
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2J1aWxkZXIudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsU0FBUyxVQUFVLGNBQWM7QUFDakMsU0FBUyxZQUFZLFVBQVU7QUFDL0IsT0FBTyxVQUFVO0FBQ2pCLFNBQVMsaUJBQWlCO0FBRTFCLFNBQVMsU0FBUyxlQUFlO0FBRWpDLElBQU07QUFBQSxFQUNKLFFBQVEsRUFBRSxNQUFNLE9BQU8sUUFBUSxZQUFZLFNBQVMsVUFBVSxRQUFRLFVBQVU7QUFDbEYsSUFBSSxVQUFVO0FBQUEsRUFDWixTQUFTO0FBQUEsSUFDUCxNQUFNLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLFNBQVM7QUFBQSxJQUN0RCxPQUFPLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxJQUNwRCxRQUFRLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLFFBQVE7QUFBQSxJQUN2RCxZQUFZLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE9BQVU7QUFBQSxJQUM3RCxTQUFTLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE9BQVU7QUFBQSxJQUMxRCxVQUFVLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxVQUFVLE1BQU0sU0FBUyxDQUFDLEVBQUU7QUFBQSxJQUNwRSxRQUFRLEVBQUUsTUFBTSxXQUFXLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxJQUN0RCxXQUFXLEVBQUUsTUFBTSxXQUFXLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxFQUMzRDtBQUNGLENBQUM7QUFFRCxPQUFPLEdBQUcsU0FBUyxZQUFZLFNBQVMsU0FBUyw4QkFBOEI7QUFDL0UsT0FBTyxHQUFHLFVBQVUsUUFBVyxtQkFBbUI7QUFDbEQsT0FBTyxHQUFHLFdBQVcsUUFBVyxvQkFBb0I7QUFFcEQsSUFBTSxnQkFBZ0IsTUFBTSxRQUFRO0FBQUEsRUFDbEM7QUFBQSxFQUNBLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxHQUFHLEtBQUs7QUFBQSxFQUNyQyxRQUFRLEtBQUssS0FBSyxRQUFRLElBQUksR0FBRyxNQUFNO0FBQUEsRUFDdkM7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQ0YsQ0FBQztBQUdELE1BQU0sUUFBUTtBQUFBLEVBQ1osY0FBYyxZQUFZLElBQUksT0FBTyxTQUFTO0FBQzVDLFVBQU0sR0FBRyxNQUFNLEtBQUssS0FBSyxLQUFLLFFBQVEsS0FBSyxJQUFJLENBQUMsR0FBRyxFQUFFLFdBQVcsS0FBSyxDQUFDO0FBQ3RFLFVBQU0sR0FBRyxVQUFVLEtBQUssTUFBTSxLQUFLLElBQUk7QUFBQSxFQUN6QyxDQUFDO0FBQ0g7QUFHQSxJQUFJLGNBQWMsYUFBYSxRQUFXO0FBQ3hDLFFBQU0sV0FBVyxRQUFRLGNBQWMsUUFBUTtBQUMvQyxRQUFNLEdBQUcsVUFBVSxLQUFLLEtBQUssUUFBUSxlQUFlLEdBQUcsS0FBSyxVQUFVLGNBQWMsVUFBVSxRQUFXLENBQUMsQ0FBQztBQUczRyxVQUFRO0FBQUEsSUFDTixHQUFHLE9BQU8sU0FBUyxTQUFTLFdBQVcsa0JBQWtCLFNBQVMsV0FBVyxXQUFXLFNBQVMsVUFBVTtBQUFBLEVBQzdHO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
@@ -0,0 +1,182 @@
|
|
1
|
+
// src/compile.ts
|
2
|
+
import { strict as assert } from "node:assert";
|
3
|
+
import { promises as fs } from "node:fs";
|
4
|
+
import path from "node:path";
|
5
|
+
import typescript from "typescript";
|
6
|
+
import { build } from "esbuild";
|
7
|
+
var commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module";
|
8
|
+
import { fileURLToPath as __fileURLToPath } from "node:url";
|
9
|
+
import { default as __path } from "node:path";
|
10
|
+
const __filename = __fileURLToPath(import.meta.url);
|
11
|
+
const __dirname = __path.dirname(__filename);
|
12
|
+
const require = __createRequire(import.meta.url);`;
|
13
|
+
async function getFiles(directory) {
|
14
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
15
|
+
const files = await Promise.all(
|
16
|
+
entries.map((entry) => {
|
17
|
+
const result = path.resolve(directory, entry.name);
|
18
|
+
return entry.isDirectory() ? getFiles(result) : result;
|
19
|
+
})
|
20
|
+
);
|
21
|
+
return files.flat();
|
22
|
+
}
|
23
|
+
function excludeSourceMaps(filter) {
|
24
|
+
return (pluginBuild) => {
|
25
|
+
pluginBuild.onLoad({ filter }, async (args) => {
|
26
|
+
if (args.path.endsWith(".js") || args.path.endsWith(".mjs")) {
|
27
|
+
return {
|
28
|
+
contents: `${await fs.readFile(
|
29
|
+
args.path,
|
30
|
+
"utf8"
|
31
|
+
)}
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==`,
|
33
|
+
loader: "default"
|
34
|
+
};
|
35
|
+
}
|
36
|
+
return void 0;
|
37
|
+
});
|
38
|
+
};
|
39
|
+
}
|
40
|
+
function resolveTypescriptPaths() {
|
41
|
+
return (pluginBuild) => {
|
42
|
+
pluginBuild.onResolve({ filter: /.*/u }, async (resolved) => {
|
43
|
+
if (resolved.kind === "entry-point" || !resolved.path.startsWith(".") || resolved.path.endsWith(".js") || resolved.path.endsWith(".json")) {
|
44
|
+
return { external: resolved.kind !== "entry-point" };
|
45
|
+
}
|
46
|
+
let isDirectory = false;
|
47
|
+
try {
|
48
|
+
const stats = await fs.lstat(path.join(resolved.resolveDir, resolved.path));
|
49
|
+
isDirectory = stats.isDirectory();
|
50
|
+
} catch {
|
51
|
+
}
|
52
|
+
let newPath = resolved.path;
|
53
|
+
newPath += isDirectory ? `/index.mjs` : `.mjs`;
|
54
|
+
return { path: newPath, external: true };
|
55
|
+
});
|
56
|
+
};
|
57
|
+
}
|
58
|
+
async function compile_default({
|
59
|
+
type,
|
60
|
+
entryPoint,
|
61
|
+
inDir,
|
62
|
+
outDir,
|
63
|
+
outFile,
|
64
|
+
external = [],
|
65
|
+
minify = false,
|
66
|
+
sourceMap,
|
67
|
+
workingDirectory = process.cwd()
|
68
|
+
}) {
|
69
|
+
const messages = [];
|
70
|
+
assert.ok(
|
71
|
+
entryPoint === void 0 && outFile === void 0 || entryPoint !== void 0 && outFile !== void 0,
|
72
|
+
"entryPoint and outFile must both be provided"
|
73
|
+
);
|
74
|
+
const allSourceFiles = await getFiles(inDir);
|
75
|
+
const productionSourceFiles = entryPoint === void 0 ? allSourceFiles.filter((file) => file.endsWith(".ts")) : [path.join(inDir, entryPoint)];
|
76
|
+
const program = typescript.createProgram(productionSourceFiles, {
|
77
|
+
module: typescript.ModuleKind.ESNext,
|
78
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
79
|
+
target: typescript.ScriptTarget.ESNext,
|
80
|
+
declaration: true,
|
81
|
+
noEmit: type !== "types",
|
82
|
+
noEmitOnError: true,
|
83
|
+
emitDeclarationOnly: type === "types",
|
84
|
+
rootDir: inDir,
|
85
|
+
outDir,
|
86
|
+
noLib: false,
|
87
|
+
skipLibCheck: true,
|
88
|
+
strict: true,
|
89
|
+
preserveConstEnums: true,
|
90
|
+
noImplicitReturns: true,
|
91
|
+
noUnusedLocals: true,
|
92
|
+
noUnusedParameters: true,
|
93
|
+
alwaysStrict: true,
|
94
|
+
verbatimModuleSyntax: true,
|
95
|
+
forceConsistentCasingInFileNames: true,
|
96
|
+
emitDecoratorMetadata: true,
|
97
|
+
experimentalDecorators: true,
|
98
|
+
resolveJsonModule: true,
|
99
|
+
esModuleInterop: true,
|
100
|
+
noUncheckedIndexedAccess: true,
|
101
|
+
noPropertyAccessFromIndexSignature: true,
|
102
|
+
allowUnusedLabels: false,
|
103
|
+
allowUnreachableCode: false,
|
104
|
+
noImplicitOverride: true,
|
105
|
+
useUnknownInCatchVariables: true,
|
106
|
+
exactOptionalPropertyTypes: true
|
107
|
+
});
|
108
|
+
const declarationFiles = [];
|
109
|
+
const emitResult = program.emit(void 0, (fileName, data) => {
|
110
|
+
declarationFiles.push({ path: fileName, text: data });
|
111
|
+
});
|
112
|
+
const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
|
113
|
+
...typescript.getPreEmitDiagnostics(program),
|
114
|
+
...emitResult.diagnostics
|
115
|
+
]);
|
116
|
+
for (const diagnostic of allDiagnostics) {
|
117
|
+
if (diagnostic.file) {
|
118
|
+
assert.ok(diagnostic.start !== void 0);
|
119
|
+
const { line, character } = typescript.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
120
|
+
const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
121
|
+
messages.push(`tsc: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
122
|
+
} else {
|
123
|
+
messages.push(`tsc: ${typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
if (messages.length > 0) {
|
127
|
+
throw new Error(`tsc failed ${JSON.stringify(messages)}`);
|
128
|
+
}
|
129
|
+
if (type === "types") {
|
130
|
+
return {
|
131
|
+
outputFiles: declarationFiles
|
132
|
+
};
|
133
|
+
}
|
134
|
+
const buildResult = await build({
|
135
|
+
entryPoints: productionSourceFiles,
|
136
|
+
bundle: true,
|
137
|
+
minify,
|
138
|
+
absWorkingDir: workingDirectory,
|
139
|
+
platform: "node",
|
140
|
+
format: "esm",
|
141
|
+
treeShaking: type === "module",
|
142
|
+
write: false,
|
143
|
+
metafile: outFile !== void 0,
|
144
|
+
sourcesContent: false,
|
145
|
+
banner: type === "module" && outFile !== void 0 ? {
|
146
|
+
js: commonJsCompatabilityBanner
|
147
|
+
} : {},
|
148
|
+
sourcemap: sourceMap ? "inline" : false,
|
149
|
+
...outFile === void 0 ? {
|
150
|
+
// individual files
|
151
|
+
outdir: outDir,
|
152
|
+
outExtension: { ".js": ".mjs" },
|
153
|
+
plugins: [
|
154
|
+
{
|
155
|
+
name: "resolve-typescript-paths",
|
156
|
+
setup: resolveTypescriptPaths()
|
157
|
+
}
|
158
|
+
]
|
159
|
+
} : {
|
160
|
+
// bundling
|
161
|
+
outfile: path.join(outDir, outFile),
|
162
|
+
legalComments: "none",
|
163
|
+
external,
|
164
|
+
plugins: [
|
165
|
+
{
|
166
|
+
name: "exclude-source-maps",
|
167
|
+
setup: excludeSourceMaps(/node_modules/u)
|
168
|
+
}
|
169
|
+
]
|
170
|
+
}
|
171
|
+
});
|
172
|
+
messages.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
|
173
|
+
messages.push(...buildResult.warnings.map((warning) => `esbuild warning: ${warning.text}`));
|
174
|
+
if (messages.length > 0) {
|
175
|
+
throw new Error(`esbuild failed ${JSON.stringify(messages)}`);
|
176
|
+
}
|
177
|
+
return buildResult;
|
178
|
+
}
|
179
|
+
export {
|
180
|
+
compile_default as default
|
181
|
+
};
|
182
|
+
//# sourceMappingURL=data:application/json;base64,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
|
@@ -0,0 +1,10 @@
|
|
1
|
+
// src/index.ts
|
2
|
+
export * from "./analyze.mjs";
|
3
|
+
export * from "./compile.mjs";
|
4
|
+
import { default as default2 } from "./analyze.mjs";
|
5
|
+
import { default as default3 } from "./compile.mjs";
|
6
|
+
export {
|
7
|
+
default2 as analyze,
|
8
|
+
default3 as compile
|
9
|
+
};
|
10
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQUVBLGNBQWM7QUFDZCxjQUFjO0FBRWQsU0FBb0IsV0FBWEEsZ0JBQTBCO0FBQ25DLFNBQW9CLFdBQVhBLGdCQUEwQjsiLAogICJuYW1lcyI6IFsiZGVmYXVsdCJdCn0K
|
@@ -0,0 +1,6 @@
|
|
1
|
+
// src/test/module-directory/index.ts
|
2
|
+
var module_directory_default = () => "module-directory-index";
|
3
|
+
export {
|
4
|
+
module_directory_default as default
|
5
|
+
};
|
6
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL3Rlc3QvbW9kdWxlLWRpcmVjdG9yeS9pbmRleC50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFFQSxJQUFPLDJCQUFRLE1BQU07IiwKICAibmFtZXMiOiBbXQp9Cg==
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,81 @@
|
|
1
|
+
export type ImportKind = 'entry-point' | 'import-statement' | 'require-call' | 'dynamic-import' | 'require-resolve' | 'import-rule' | 'composes-from' | 'url-token';
|
2
|
+
export interface Metafile {
|
3
|
+
inputs: {
|
4
|
+
[path: string]: {
|
5
|
+
bytes: number;
|
6
|
+
imports: {
|
7
|
+
path: string;
|
8
|
+
kind: ImportKind;
|
9
|
+
external?: boolean;
|
10
|
+
original?: string;
|
11
|
+
}[];
|
12
|
+
format?: 'cjs' | 'esm';
|
13
|
+
};
|
14
|
+
};
|
15
|
+
outputs: {
|
16
|
+
[path: string]: {
|
17
|
+
bytes: number;
|
18
|
+
inputs: {
|
19
|
+
[path: string]: {
|
20
|
+
bytesInOutput: number;
|
21
|
+
};
|
22
|
+
};
|
23
|
+
imports: {
|
24
|
+
path: string;
|
25
|
+
kind: ImportKind | 'file-loader';
|
26
|
+
external?: boolean;
|
27
|
+
}[];
|
28
|
+
exports: string[];
|
29
|
+
entryPoint?: string;
|
30
|
+
cssBundle?: string;
|
31
|
+
};
|
32
|
+
};
|
33
|
+
}
|
34
|
+
export interface OutputFile {
|
35
|
+
path: string;
|
36
|
+
text: string;
|
37
|
+
}
|
38
|
+
export interface CompileResult {
|
39
|
+
metafile?: Metafile | undefined;
|
40
|
+
outputFiles: OutputFile[];
|
41
|
+
}
|
42
|
+
export interface CompileOptions {
|
43
|
+
/**
|
44
|
+
* whether to produce Typescript types or ESM code
|
45
|
+
*/
|
46
|
+
type: 'module' | 'types';
|
47
|
+
/**
|
48
|
+
* the entry point for the bundle, relative to the inDir. if not provided, the files in the inDir will be processed
|
49
|
+
* as individual unbundled files
|
50
|
+
*/
|
51
|
+
entryPoint?: string | undefined;
|
52
|
+
/**
|
53
|
+
* source code
|
54
|
+
*/
|
55
|
+
inDir: string;
|
56
|
+
/**
|
57
|
+
* build directory
|
58
|
+
*/
|
59
|
+
outDir: string;
|
60
|
+
/**
|
61
|
+
* build file, relative to the outDir
|
62
|
+
*/
|
63
|
+
outFile?: string | undefined;
|
64
|
+
/**
|
65
|
+
* external modules to exclude from the bundle
|
66
|
+
*/
|
67
|
+
external?: string[] | undefined;
|
68
|
+
/**
|
69
|
+
* whether to minify output
|
70
|
+
*/
|
71
|
+
minify?: boolean | undefined;
|
72
|
+
/**
|
73
|
+
* whether to include sourcemap
|
74
|
+
*/
|
75
|
+
sourceMap?: boolean | undefined;
|
76
|
+
/**
|
77
|
+
* working directory
|
78
|
+
*/
|
79
|
+
workingDirectory?: string | undefined;
|
80
|
+
}
|
81
|
+
export default function ({ type, entryPoint, inDir, outDir, outFile, external, minify, sourceMap, workingDirectory, }: CompileOptions): Promise<CompileResult>;
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"name":"@checkdigit/typescript-config","version":"7.0.0-PR.54-
|
1
|
+
{"name":"@checkdigit/typescript-config","version":"7.0.0-PR.54-950f","description":"Check Digit standard Typescript configuration","prettier":"@checkdigit/prettier-config","engines":{"node":">=20.11"},"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","import":"./dist-mjs/index.mjs","default":"./tsconfig.json"}},"bin":{"builder":"./bin/builder.mjs"},"peerDependencies":{"@types/node":">=20.11","esbuild":"0.20.0","typescript":"5.4.0-beta"},"repository":{"type":"git","url":"git+https://github.com/checkdigit/typescript-config.git"},"author":"Check Digit, LLC","license":"MIT","bugs":{"url":"https://github.com/checkdigit/typescript-config/issues"},"homepage":"https://github.com/checkdigit/typescript-config#readme","scripts":{"prepublishOnly":"npm run build-builder && npm run build:dist-types && npm run build:dist-mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","lint:fix":"eslint --ignore-path .gitignore . --fix","lint":"eslint --max-warnings 0 --ignore-path .gitignore .","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style","build-builder":"esbuild src/builder.ts --bundle --platform=node --format=esm --external:typescript --external:esbuild --outfile=build-builder/builder.mjs && mkdir -p bin && { echo '#!/usr/bin/env node'; cat build-builder/builder.mjs; } > bin/builder.mjs && chmod +x bin/builder.mjs","build-types":"rimraf build-types && bin/builder.mjs --type=types --outDir=build-types","build-mjs":"rimraf build-mjs && bin/builder.mjs --type=module --outDir=build-mjs","build-mjs-bundle":"rimraf build-mjs-bundle && bin/builder.mjs --type=module --outDir=build-mjs-bundle --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-minify":"rimraf build-mjs-bundle-minify && bin/builder.mjs --type=module --minify --outDir=build-mjs-bundle-minify --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-no-external":"rimraf build-mjs-bundle-no-external && bin/builder.mjs --type=module --external=./node_modules/* --outDir=build-mjs-bundle-no-external --entryPoint=test/index.test.ts --outFile=test/index.test.mjs --minify","test-jest-mjs":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","test-mjs":"node --test build-mjs/test/index.test.mjs","test-mjs-bundle":"node --test build-mjs-bundle/test/index.test.mjs","test-mjs-bundle-minify":"node --test build-mjs-bundle-minify/test/index.test.mjs","test-mjs-bundle-no-external":"node --test build-mjs-bundle-no-external/test/index.test.mjs","ci:test":"npm run test-jest-mjs && npm run test-mjs && npm run test-mjs-bundle && npm run test-mjs-bundle-no-external","ci:compile":"tsc --noEmit && npm run build-builder && npm run build-types && npm run build-mjs && npm run build-mjs-bundle && npm run build-mjs-bundle-minify && npm run build-mjs-bundle-no-external","ci:lint":"npm run lint","ci:style":"npm run prettier"},"devDependencies":{"@apidevtools/json-schema-ref-parser":"^11.1.0","@checkdigit/prettier-config":"^5.2.0","@types/debug":"^4.1.12","@types/jest":"^29.5.11","@types/uuid":"^9.0.8","@typescript-eslint/eslint-plugin":"^6.20.0","@typescript-eslint/parser":"^6.20.0","debug":"^4.3.4","eslint":"^8.56.0","eslint-config-prettier":"^9.1.0","jest":"^29.7.0","node-fetch":"^3.3.2","rimraf":"^5.0.5","ts-jest":"^29.1.2","uuid":"^9.0.1"},"eslintConfig":{"parser":"@typescript-eslint/parser","plugins":["@typescript-eslint"],"parserOptions":{"project":"./tsconfig.json"},"extends":["eslint:all","plugin:@typescript-eslint/recommended","plugin:@typescript-eslint/recommended-requiring-type-checking","plugin:@typescript-eslint/strict","prettier"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"error","capitalized-comments":"off","one-var":"off","sort-keys":"off","sort-imports":"off","max-lines":["error",{"max":500,"skipBlankLines":true,"skipComments":true}],"func-style":["error","declaration",{"allowArrowFunctions":true}],"no-magic-numbers":["error",{"ignore":[0,1,2]}],"no-undefined":"off","no-ternary":"off"},"overrides":[{"files":["*.spec.ts","*.test.ts"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"off","@typescript-eslint/ban-types":"off","@typescript-eslint/require-await":"off","@typescript-eslint/consistent-type-definitions":"off","@typescript-eslint/ban-ts-comment":"off","@typescript-eslint/no-unnecessary-condition":"off","@typescript-eslint/consistent-indexed-object-style":"off","@typescript-eslint/no-unused-vars":"off","@typescript-eslint/no-unsafe-member-access":"off","line-comment-position":"off","no-fallthrough":"off","no-inline-comments":"off","no-param-reassign":"off","id-length":"off","no-magic-numbers":"off","func-names":"off","no-duplicate-imports":"off","symbol-description":"off","no-invalid-this":"off","max-lines-per-function":"off","max-lines":"off","max-statements":"off","no-await-in-loop":"off"}}]},"jest":{"moduleFileExtensions":["js","mjs","cjs","ts","json","node"],"extensionsToTreatAsEsm":[".ts"],"transform":{"^.+\\.ts$":["ts-jest",{"isolatedModules":true,"diagnostics":false,"useESM":true}]},"collectCoverageFrom":["<rootDir>/src/**"],"testMatch":["<rootDir>/src/**/*.spec.ts"]},"files":["bin","tsconfig.json","src","dist-types","dist-mjs","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"overrides":{"typescript":"5.4.0-beta"}}
|
package/src/analyze.ts
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
// analyze.ts
|
2
|
+
|
3
|
+
import { strict as assert } from 'node:assert';
|
4
|
+
|
5
|
+
import type { Metafile } from 'esbuild';
|
6
|
+
|
7
|
+
export default function analyze(metafile: Metafile) {
|
8
|
+
const source = new Set(Object.keys(metafile.inputs).filter((key) => !key.startsWith('node_modules')));
|
9
|
+
const modules = new Set(Object.keys(metafile.inputs).filter((key) => key.startsWith('node_modules')));
|
10
|
+
|
11
|
+
const [output] = Object.entries(metafile.outputs);
|
12
|
+
assert.ok(output !== undefined);
|
13
|
+
const [, bundle] = output;
|
14
|
+
|
15
|
+
const sourceBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
|
16
|
+
if (source.has(file)) {
|
17
|
+
return bytes + value.bytesInOutput;
|
18
|
+
}
|
19
|
+
return bytes;
|
20
|
+
}, 0);
|
21
|
+
|
22
|
+
const moduleBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
|
23
|
+
if (modules.has(file)) {
|
24
|
+
return bytes + value.bytesInOutput;
|
25
|
+
}
|
26
|
+
return bytes;
|
27
|
+
}, 0);
|
28
|
+
|
29
|
+
return {
|
30
|
+
sourceBytes,
|
31
|
+
moduleBytes,
|
32
|
+
totalBytes: bundle.bytes,
|
33
|
+
};
|
34
|
+
}
|
package/src/builder.ts
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
// builder.ts
|
2
|
+
|
3
|
+
import { strict as assert } from 'node:assert';
|
4
|
+
import { promises as fs } from 'node:fs';
|
5
|
+
import path from 'node:path';
|
6
|
+
import { parseArgs } from 'node:util';
|
7
|
+
|
8
|
+
import { compile, analyze } from './index';
|
9
|
+
|
10
|
+
const {
|
11
|
+
values: { type, inDir, outDir, entryPoint, outFile, external, minify, sourceMap },
|
12
|
+
} = parseArgs({
|
13
|
+
options: {
|
14
|
+
type: { type: 'string', short: 't', default: 'module' },
|
15
|
+
inDir: { type: 'string', short: 'i', default: 'src' },
|
16
|
+
outDir: { type: 'string', short: 'o', default: 'build' },
|
17
|
+
entryPoint: { type: 'string', short: 'e', default: undefined },
|
18
|
+
outFile: { type: 'string', short: 'f', default: undefined },
|
19
|
+
external: { type: 'string', short: 'x', multiple: true, default: [] },
|
20
|
+
minify: { type: 'boolean', short: 'm', default: false },
|
21
|
+
sourceMap: { type: 'boolean', short: 's', default: false },
|
22
|
+
},
|
23
|
+
});
|
24
|
+
|
25
|
+
assert.ok(type === 'module' || type === 'types', 'type must be types or module');
|
26
|
+
assert.ok(inDir !== undefined, 'inDir is required');
|
27
|
+
assert.ok(outDir !== undefined, 'outDir is required');
|
28
|
+
|
29
|
+
const compileResult = await compile({
|
30
|
+
type,
|
31
|
+
inDir: path.join(process.cwd(), inDir),
|
32
|
+
outDir: path.join(process.cwd(), outDir),
|
33
|
+
entryPoint,
|
34
|
+
outFile,
|
35
|
+
external,
|
36
|
+
minify,
|
37
|
+
sourceMap,
|
38
|
+
});
|
39
|
+
|
40
|
+
// write output files
|
41
|
+
await Promise.all(
|
42
|
+
compileResult.outputFiles.map(async (file) => {
|
43
|
+
await fs.mkdir(path.join(path.dirname(file.path)), { recursive: true });
|
44
|
+
await fs.writeFile(file.path, file.text);
|
45
|
+
}),
|
46
|
+
);
|
47
|
+
|
48
|
+
// write metafile.json
|
49
|
+
if (compileResult.metafile !== undefined) {
|
50
|
+
const analysis = analyze(compileResult.metafile);
|
51
|
+
await fs.writeFile(path.join(outDir, 'metafile.json'), JSON.stringify(compileResult.metafile, undefined, 2));
|
52
|
+
|
53
|
+
// eslint-disable-next-line no-console
|
54
|
+
console.log(
|
55
|
+
`${outFile}: src ${analysis.sourceBytes}, node_modules ${analysis.moduleBytes}, total ${analysis.totalBytes}`,
|
56
|
+
);
|
57
|
+
}
|
package/src/compile.ts
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
// compile.ts
|
2
|
+
|
3
|
+
import { strict as assert } from 'node:assert';
|
4
|
+
import { promises as fs } from 'node:fs';
|
5
|
+
import path from 'node:path';
|
6
|
+
|
7
|
+
import typescript from 'typescript';
|
8
|
+
import { type PluginBuild, build } from 'esbuild';
|
9
|
+
|
10
|
+
const commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module";
|
11
|
+
import { fileURLToPath as __fileURLToPath } from "node:url";
|
12
|
+
import { default as __path } from "node:path";
|
13
|
+
const __filename = __fileURLToPath(import.meta.url);
|
14
|
+
const __dirname = __path.dirname(__filename);
|
15
|
+
const require = __createRequire(import.meta.url);`;
|
16
|
+
|
17
|
+
export type ImportKind =
|
18
|
+
| 'entry-point'
|
19
|
+
| 'import-statement'
|
20
|
+
| 'require-call'
|
21
|
+
| 'dynamic-import'
|
22
|
+
| 'require-resolve'
|
23
|
+
| 'import-rule'
|
24
|
+
| 'composes-from'
|
25
|
+
| 'url-token';
|
26
|
+
|
27
|
+
export interface Metafile {
|
28
|
+
inputs: {
|
29
|
+
[path: string]: {
|
30
|
+
bytes: number;
|
31
|
+
imports: {
|
32
|
+
path: string;
|
33
|
+
kind: ImportKind;
|
34
|
+
external?: boolean;
|
35
|
+
original?: string;
|
36
|
+
}[];
|
37
|
+
format?: 'cjs' | 'esm';
|
38
|
+
};
|
39
|
+
};
|
40
|
+
outputs: {
|
41
|
+
[path: string]: {
|
42
|
+
bytes: number;
|
43
|
+
inputs: {
|
44
|
+
[path: string]: {
|
45
|
+
bytesInOutput: number;
|
46
|
+
};
|
47
|
+
};
|
48
|
+
imports: {
|
49
|
+
path: string;
|
50
|
+
kind: ImportKind | 'file-loader';
|
51
|
+
external?: boolean;
|
52
|
+
}[];
|
53
|
+
exports: string[];
|
54
|
+
entryPoint?: string;
|
55
|
+
cssBundle?: string;
|
56
|
+
};
|
57
|
+
};
|
58
|
+
}
|
59
|
+
|
60
|
+
export interface OutputFile {
|
61
|
+
path: string;
|
62
|
+
text: string;
|
63
|
+
}
|
64
|
+
|
65
|
+
export interface CompileResult {
|
66
|
+
metafile?: Metafile | undefined;
|
67
|
+
outputFiles: OutputFile[];
|
68
|
+
}
|
69
|
+
|
70
|
+
export interface CompileOptions {
|
71
|
+
/**
|
72
|
+
* whether to produce Typescript types or ESM code
|
73
|
+
*/
|
74
|
+
type: 'module' | 'types';
|
75
|
+
|
76
|
+
/**
|
77
|
+
* the entry point for the bundle, relative to the inDir. if not provided, the files in the inDir will be processed
|
78
|
+
* as individual unbundled files
|
79
|
+
*/
|
80
|
+
entryPoint?: string | undefined;
|
81
|
+
|
82
|
+
/**
|
83
|
+
* source code
|
84
|
+
*/
|
85
|
+
inDir: string;
|
86
|
+
|
87
|
+
/**
|
88
|
+
* build directory
|
89
|
+
*/
|
90
|
+
outDir: string;
|
91
|
+
|
92
|
+
/**
|
93
|
+
* build file, relative to the outDir
|
94
|
+
*/
|
95
|
+
outFile?: string | undefined;
|
96
|
+
|
97
|
+
/**
|
98
|
+
* external modules to exclude from the bundle
|
99
|
+
*/
|
100
|
+
external?: string[] | undefined;
|
101
|
+
|
102
|
+
/**
|
103
|
+
* whether to minify output
|
104
|
+
*/
|
105
|
+
minify?: boolean | undefined;
|
106
|
+
|
107
|
+
/**
|
108
|
+
* whether to include sourcemap
|
109
|
+
*/
|
110
|
+
sourceMap?: boolean | undefined;
|
111
|
+
|
112
|
+
/**
|
113
|
+
* working directory
|
114
|
+
*/
|
115
|
+
workingDirectory?: string | undefined;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Recursively obtains all files in a directory
|
120
|
+
* @param {string} directory
|
121
|
+
* @returns {Promise<string[]>}
|
122
|
+
*/
|
123
|
+
async function getFiles(directory: string): Promise<string[]> {
|
124
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
125
|
+
const files = await Promise.all(
|
126
|
+
entries.map((entry) => {
|
127
|
+
const result = path.resolve(directory, entry.name);
|
128
|
+
return entry.isDirectory() ? getFiles(result) : result;
|
129
|
+
}),
|
130
|
+
);
|
131
|
+
return files.flat();
|
132
|
+
}
|
133
|
+
|
134
|
+
function excludeSourceMaps(filter: RegExp) {
|
135
|
+
return (pluginBuild: PluginBuild) => {
|
136
|
+
// ignore source maps for any Javascript file that matches filter
|
137
|
+
pluginBuild.onLoad({ filter }, async (args) => {
|
138
|
+
if (args.path.endsWith('.js') || args.path.endsWith('.mjs')) {
|
139
|
+
return {
|
140
|
+
contents: `${await fs.readFile(
|
141
|
+
args.path,
|
142
|
+
'utf8',
|
143
|
+
)}\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==`,
|
144
|
+
loader: 'default',
|
145
|
+
};
|
146
|
+
}
|
147
|
+
return undefined;
|
148
|
+
});
|
149
|
+
};
|
150
|
+
}
|
151
|
+
|
152
|
+
function resolveTypescriptPaths() {
|
153
|
+
return (pluginBuild: PluginBuild) => {
|
154
|
+
// rewrite paths based on standard node resolution
|
155
|
+
pluginBuild.onResolve({ filter: /.*/u }, async (resolved) => {
|
156
|
+
if (
|
157
|
+
resolved.kind === 'entry-point' ||
|
158
|
+
!resolved.path.startsWith('.') ||
|
159
|
+
resolved.path.endsWith('.js') ||
|
160
|
+
resolved.path.endsWith('.json')
|
161
|
+
) {
|
162
|
+
return { external: resolved.kind !== 'entry-point' };
|
163
|
+
}
|
164
|
+
let isDirectory = false;
|
165
|
+
try {
|
166
|
+
const stats = await fs.lstat(path.join(resolved.resolveDir, resolved.path));
|
167
|
+
isDirectory = stats.isDirectory();
|
168
|
+
} catch {
|
169
|
+
// do nothing
|
170
|
+
}
|
171
|
+
let newPath = resolved.path;
|
172
|
+
newPath += isDirectory ? `/index.mjs` : `.mjs`;
|
173
|
+
return { path: newPath, external: true };
|
174
|
+
});
|
175
|
+
};
|
176
|
+
}
|
177
|
+
|
178
|
+
// eslint-disable-next-line func-names,max-lines-per-function,max-statements
|
179
|
+
export default async function ({
|
180
|
+
type,
|
181
|
+
entryPoint,
|
182
|
+
inDir,
|
183
|
+
outDir,
|
184
|
+
outFile,
|
185
|
+
external = [],
|
186
|
+
minify = false,
|
187
|
+
sourceMap,
|
188
|
+
workingDirectory = process.cwd(),
|
189
|
+
}: CompileOptions): Promise<CompileResult> {
|
190
|
+
const messages: string[] = [];
|
191
|
+
|
192
|
+
assert.ok(
|
193
|
+
(entryPoint === undefined && outFile === undefined) || (entryPoint !== undefined && outFile !== undefined),
|
194
|
+
'entryPoint and outFile must both be provided',
|
195
|
+
);
|
196
|
+
|
197
|
+
const allSourceFiles = await getFiles(inDir);
|
198
|
+
const productionSourceFiles =
|
199
|
+
entryPoint === undefined ? allSourceFiles.filter((file) => file.endsWith('.ts')) : [path.join(inDir, entryPoint)];
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Emit declarations using typescript compiler, if type is 'types'. Otherwise, just compile to ensure the build is good.
|
203
|
+
*/
|
204
|
+
const program = typescript.createProgram(productionSourceFiles, {
|
205
|
+
module: typescript.ModuleKind.ESNext,
|
206
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
207
|
+
target: typescript.ScriptTarget.ESNext,
|
208
|
+
declaration: true,
|
209
|
+
noEmit: type !== 'types',
|
210
|
+
noEmitOnError: true,
|
211
|
+
emitDeclarationOnly: type === 'types',
|
212
|
+
rootDir: inDir,
|
213
|
+
outDir,
|
214
|
+
noLib: false,
|
215
|
+
skipLibCheck: true,
|
216
|
+
strict: true,
|
217
|
+
preserveConstEnums: true,
|
218
|
+
noImplicitReturns: true,
|
219
|
+
noUnusedLocals: true,
|
220
|
+
noUnusedParameters: true,
|
221
|
+
alwaysStrict: true,
|
222
|
+
verbatimModuleSyntax: true,
|
223
|
+
forceConsistentCasingInFileNames: true,
|
224
|
+
emitDecoratorMetadata: true,
|
225
|
+
experimentalDecorators: true,
|
226
|
+
resolveJsonModule: true,
|
227
|
+
esModuleInterop: true,
|
228
|
+
noUncheckedIndexedAccess: true,
|
229
|
+
noPropertyAccessFromIndexSignature: true,
|
230
|
+
allowUnusedLabels: false,
|
231
|
+
allowUnreachableCode: false,
|
232
|
+
noImplicitOverride: true,
|
233
|
+
useUnknownInCatchVariables: true,
|
234
|
+
exactOptionalPropertyTypes: true,
|
235
|
+
});
|
236
|
+
const declarationFiles: OutputFile[] = [];
|
237
|
+
const emitResult = program.emit(undefined, (fileName, data) => {
|
238
|
+
declarationFiles.push({ path: fileName, text: data });
|
239
|
+
});
|
240
|
+
const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
|
241
|
+
...typescript.getPreEmitDiagnostics(program),
|
242
|
+
...emitResult.diagnostics,
|
243
|
+
]);
|
244
|
+
for (const diagnostic of allDiagnostics) {
|
245
|
+
if (diagnostic.file) {
|
246
|
+
assert.ok(diagnostic.start !== undefined);
|
247
|
+
const { line, character } = typescript.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
248
|
+
const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
249
|
+
messages.push(`tsc: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
250
|
+
} else {
|
251
|
+
// eslint-disable-next-line no-console
|
252
|
+
messages.push(`tsc: ${typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
if (messages.length > 0) {
|
257
|
+
throw new Error(`tsc failed ${JSON.stringify(messages)}`);
|
258
|
+
}
|
259
|
+
|
260
|
+
if (type === 'types') {
|
261
|
+
return {
|
262
|
+
outputFiles: declarationFiles,
|
263
|
+
};
|
264
|
+
}
|
265
|
+
|
266
|
+
/**
|
267
|
+
* Emit ESM javascript using esbuild
|
268
|
+
*/
|
269
|
+
const buildResult = await build({
|
270
|
+
entryPoints: productionSourceFiles,
|
271
|
+
bundle: true,
|
272
|
+
minify,
|
273
|
+
absWorkingDir: workingDirectory,
|
274
|
+
platform: 'node',
|
275
|
+
format: 'esm',
|
276
|
+
treeShaking: type === 'module',
|
277
|
+
write: false,
|
278
|
+
metafile: outFile !== undefined,
|
279
|
+
sourcesContent: false,
|
280
|
+
banner:
|
281
|
+
type === 'module' && outFile !== undefined
|
282
|
+
? {
|
283
|
+
js: commonJsCompatabilityBanner,
|
284
|
+
}
|
285
|
+
: {},
|
286
|
+
sourcemap: sourceMap ? 'inline' : false,
|
287
|
+
...(outFile === undefined
|
288
|
+
? {
|
289
|
+
// individual files
|
290
|
+
outdir: outDir,
|
291
|
+
outExtension: { '.js': '.mjs' },
|
292
|
+
plugins: [
|
293
|
+
{
|
294
|
+
name: 'resolve-typescript-paths',
|
295
|
+
setup: resolveTypescriptPaths(),
|
296
|
+
},
|
297
|
+
],
|
298
|
+
}
|
299
|
+
: {
|
300
|
+
// bundling
|
301
|
+
outfile: path.join(outDir, outFile),
|
302
|
+
legalComments: 'none',
|
303
|
+
external,
|
304
|
+
plugins: [
|
305
|
+
{
|
306
|
+
name: 'exclude-source-maps',
|
307
|
+
setup: excludeSourceMaps(/node_modules/u),
|
308
|
+
},
|
309
|
+
],
|
310
|
+
}),
|
311
|
+
});
|
312
|
+
|
313
|
+
messages.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
|
314
|
+
messages.push(...buildResult.warnings.map((warning) => `esbuild warning: ${warning.text}`));
|
315
|
+
if (messages.length > 0) {
|
316
|
+
throw new Error(`esbuild failed ${JSON.stringify(messages)}`);
|
317
|
+
}
|
318
|
+
|
319
|
+
return buildResult;
|
320
|
+
}
|
package/src/index.ts
ADDED