@checkdigit/typescript-config 7.0.0-PR.54-a1c4 → 7.0.0-PR.54-2621
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/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-2621","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":"./dist-mjs/index.mjs"}},"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