@steambrew/ttc 3.2.2 → 3.2.4
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/bun.lock +1 -0
- package/dist/index.js +61 -137
- package/package.json +3 -3
- package/rollup.config.js +6 -1
- package/src/check-health.ts +30 -0
- package/src/index.ts +31 -1
- package/src/logger.ts +36 -6
- package/src/plugin-api.ts +30 -0
- package/src/plugin-json.d.ts +30 -0
- package/src/query-parser.ts +30 -0
- package/src/static-embed.ts +78 -44
- package/src/transpiler.ts +43 -95
- package/src/version-control.ts +30 -0
- package/.claude/settings.local.json +0 -10
package/bun.lock
CHANGED
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import replace from '@rollup/plugin-replace';
|
|
|
12
12
|
import terser from '@rollup/plugin-terser';
|
|
13
13
|
import typescript from '@rollup/plugin-typescript';
|
|
14
14
|
import esbuild from 'rollup-plugin-esbuild';
|
|
15
|
+
import ts from 'typescript';
|
|
15
16
|
import url from '@rollup/plugin-url';
|
|
16
17
|
import nodePolyfills from 'rollup-plugin-polyfill-node';
|
|
17
18
|
import { watch, rollup } from 'rollup';
|
|
@@ -51,14 +52,14 @@ const Logger = {
|
|
|
51
52
|
},
|
|
52
53
|
done({ elapsedMs, buildType, sysfsCount, envCount }) {
|
|
53
54
|
const elapsed = `${(elapsedMs / 1000).toFixed(2)}s`;
|
|
54
|
-
const meta = [`ttc v${version}`];
|
|
55
|
+
const meta = [`ttc v${version} (${"fd773865"})`];
|
|
55
56
|
if (buildType === 'dev')
|
|
56
57
|
meta.push('no type checking');
|
|
57
58
|
if (sysfsCount)
|
|
58
|
-
meta.push(`${sysfsCount}
|
|
59
|
+
meta.push(`${sysfsCount} bundled file${sysfsCount > 1 ? 's' : ''}`);
|
|
59
60
|
if (envCount)
|
|
60
61
|
meta.push(`${envCount} env var${envCount > 1 ? 's' : ''}`);
|
|
61
|
-
console.log(`${chalk.green('Finished')} ${buildType} in ${elapsed} ` + chalk.dim(
|
|
62
|
+
console.log(`${chalk.green('Finished')} ${buildType} in ${elapsed} ` + chalk.dim(meta.join(', ')));
|
|
62
63
|
},
|
|
63
64
|
failed({ elapsedMs, buildType }) {
|
|
64
65
|
const elapsed = `${(elapsedMs / 1000).toFixed(2)}s`;
|
|
@@ -267,6 +268,7 @@ function constSysfsExpr(options = {}) {
|
|
|
267
268
|
const filter = createFilter(options.include, options.exclude);
|
|
268
269
|
const pluginName = 'millennium-const-sysfs-expr';
|
|
269
270
|
let count = 0;
|
|
271
|
+
const globCache = new Map();
|
|
270
272
|
const plugin = {
|
|
271
273
|
name: pluginName,
|
|
272
274
|
transform(code, id) {
|
|
@@ -310,8 +312,6 @@ function constSysfsExpr(options = {}) {
|
|
|
310
312
|
}
|
|
311
313
|
}
|
|
312
314
|
},
|
|
313
|
-
});
|
|
314
|
-
traverse(ast, {
|
|
315
315
|
CallExpression: (nodePath) => {
|
|
316
316
|
const node = nodePath.node;
|
|
317
317
|
if (node.callee.type === 'Identifier' && node.callee.name === 'constSysfsExpr') {
|
|
@@ -431,53 +431,60 @@ function constSysfsExpr(options = {}) {
|
|
|
431
431
|
? path.dirname(pathOrPattern)
|
|
432
432
|
: path.resolve(path.dirname(id), path.dirname(pathOrPattern));
|
|
433
433
|
let embeddedContent;
|
|
434
|
-
const
|
|
435
|
-
if (
|
|
436
|
-
|
|
437
|
-
fs.statSync(path.resolve(searchBasePath, pathOrPattern)).isFile()) {
|
|
438
|
-
const singleFilePath = path.resolve(searchBasePath, pathOrPattern);
|
|
439
|
-
try {
|
|
440
|
-
const rawContent = fs.readFileSync(singleFilePath, callOptions.encoding);
|
|
441
|
-
const contentString = rawContent.toString();
|
|
442
|
-
const fileInfo = {
|
|
443
|
-
content: contentString,
|
|
444
|
-
filePath: singleFilePath,
|
|
445
|
-
fileName: path.relative(searchBasePath, singleFilePath),
|
|
446
|
-
};
|
|
447
|
-
embeddedContent = JSON.stringify(fileInfo);
|
|
448
|
-
this.addWatchFile(singleFilePath);
|
|
449
|
-
}
|
|
450
|
-
catch (fileError) {
|
|
451
|
-
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
452
|
-
this.error(`Error reading file ${singleFilePath}: ${message}`, node.loc?.start.index);
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
434
|
+
const cacheKey = `${searchBasePath}\0${pathOrPattern}\0${callOptions.encoding}`;
|
|
435
|
+
if (globCache.has(cacheKey)) {
|
|
436
|
+
embeddedContent = globCache.get(cacheKey);
|
|
455
437
|
}
|
|
456
438
|
else {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const fileInfoArray = [];
|
|
463
|
-
for (const fullPath of matchingFiles) {
|
|
439
|
+
const isPotentialPattern = /[?*+!@()[\]{}]/.test(pathOrPattern);
|
|
440
|
+
if (!isPotentialPattern &&
|
|
441
|
+
fs.existsSync(path.resolve(searchBasePath, pathOrPattern)) &&
|
|
442
|
+
fs.statSync(path.resolve(searchBasePath, pathOrPattern)).isFile()) {
|
|
443
|
+
const singleFilePath = path.resolve(searchBasePath, pathOrPattern);
|
|
464
444
|
try {
|
|
465
|
-
const rawContent = fs.readFileSync(
|
|
445
|
+
const rawContent = fs.readFileSync(singleFilePath, callOptions.encoding);
|
|
466
446
|
const contentString = rawContent.toString();
|
|
467
|
-
|
|
447
|
+
const fileInfo = {
|
|
468
448
|
content: contentString,
|
|
469
|
-
filePath:
|
|
470
|
-
fileName: path.relative(searchBasePath,
|
|
471
|
-
}
|
|
472
|
-
|
|
449
|
+
filePath: singleFilePath,
|
|
450
|
+
fileName: path.relative(searchBasePath, singleFilePath),
|
|
451
|
+
};
|
|
452
|
+
embeddedContent = JSON.stringify(fileInfo);
|
|
453
|
+
this.addWatchFile(singleFilePath);
|
|
473
454
|
}
|
|
474
455
|
catch (fileError) {
|
|
475
456
|
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
476
|
-
this.
|
|
457
|
+
this.error(`Error reading file ${singleFilePath}: ${message}`, node.loc?.start.index);
|
|
458
|
+
return;
|
|
477
459
|
}
|
|
478
460
|
}
|
|
479
|
-
|
|
480
|
-
|
|
461
|
+
else {
|
|
462
|
+
const matchingFiles = glob.sync(pathOrPattern, {
|
|
463
|
+
cwd: searchBasePath,
|
|
464
|
+
nodir: true,
|
|
465
|
+
absolute: true,
|
|
466
|
+
});
|
|
467
|
+
const fileInfoArray = [];
|
|
468
|
+
for (const fullPath of matchingFiles) {
|
|
469
|
+
try {
|
|
470
|
+
const rawContent = fs.readFileSync(fullPath, callOptions.encoding);
|
|
471
|
+
const contentString = rawContent.toString();
|
|
472
|
+
fileInfoArray.push({
|
|
473
|
+
content: contentString,
|
|
474
|
+
filePath: fullPath,
|
|
475
|
+
fileName: path.relative(searchBasePath, fullPath),
|
|
476
|
+
});
|
|
477
|
+
this.addWatchFile(fullPath);
|
|
478
|
+
}
|
|
479
|
+
catch (fileError) {
|
|
480
|
+
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
481
|
+
this.warn(`Error reading file ${fullPath}: ${message}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
embeddedContent = JSON.stringify(fileInfoArray);
|
|
485
|
+
}
|
|
486
|
+
globCache.set(cacheKey, embeddedContent);
|
|
487
|
+
} // end cache miss
|
|
481
488
|
// Replace the call expression with the generated content string
|
|
482
489
|
magicString.overwrite(node.start, node.end, embeddedContent);
|
|
483
490
|
hasReplaced = true;
|
|
@@ -605,99 +612,16 @@ function stripPluginPrefix(message) {
|
|
|
605
612
|
}
|
|
606
613
|
class BuildFailedError extends Error {
|
|
607
614
|
}
|
|
608
|
-
/**
|
|
609
|
-
* tsconfig.json files use JSONC, not regular JSON.
|
|
610
|
-
* JSONC supports comments and trailing commas, while JSON does not.
|
|
611
|
-
* This function sanitizes JSONC into JSON.parse()-able content.
|
|
612
|
-
*
|
|
613
|
-
* @param text input text
|
|
614
|
-
* @returns object
|
|
615
|
-
*/
|
|
616
|
-
function parseJsonc(text) {
|
|
617
|
-
let out = '';
|
|
618
|
-
let i = 0;
|
|
619
|
-
const n = text.length;
|
|
620
|
-
while (i < n) {
|
|
621
|
-
const ch = text[i];
|
|
622
|
-
if (ch === '"') {
|
|
623
|
-
out += ch;
|
|
624
|
-
i++;
|
|
625
|
-
while (i < n) {
|
|
626
|
-
const c = text[i];
|
|
627
|
-
out += c;
|
|
628
|
-
if (c === '\\') {
|
|
629
|
-
i++;
|
|
630
|
-
if (i < n) {
|
|
631
|
-
out += text[i];
|
|
632
|
-
i++;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
else if (c === '"') {
|
|
636
|
-
i++;
|
|
637
|
-
break;
|
|
638
|
-
}
|
|
639
|
-
else
|
|
640
|
-
i++;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
else if (ch === '/' && i + 1 < n && text[i + 1] === '/') {
|
|
644
|
-
i += 2;
|
|
645
|
-
while (i < n && text[i] !== '\n')
|
|
646
|
-
i++;
|
|
647
|
-
}
|
|
648
|
-
else if (ch === '/' && i + 1 < n && text[i + 1] === '*') {
|
|
649
|
-
i += 2;
|
|
650
|
-
while (i < n - 1 && !(text[i] === '*' && text[i + 1] === '/'))
|
|
651
|
-
i++;
|
|
652
|
-
i += 2;
|
|
653
|
-
}
|
|
654
|
-
else if (ch === ',') {
|
|
655
|
-
let j = i + 1;
|
|
656
|
-
while (j < n && (text[j] === ' ' || text[j] === '\t' || text[j] === '\n' || text[j] === '\r'))
|
|
657
|
-
j++;
|
|
658
|
-
if (j < n && (text[j] === '}' || text[j] === ']')) {
|
|
659
|
-
i++;
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
out += ch;
|
|
663
|
-
i++;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
out += ch;
|
|
668
|
-
i++;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return JSON.parse(out);
|
|
672
|
-
}
|
|
673
615
|
function tsconfigPathsPlugin(tsconfigPath) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
let parentResult = { baseUrl: null, entries: [] };
|
|
684
|
-
if (raw.extends) {
|
|
685
|
-
const ext = raw.extends;
|
|
686
|
-
const parentPath = path.resolve(dir, ext.endsWith('.json') ? ext : `${ext}.json`);
|
|
687
|
-
parentResult = readConfig(parentPath);
|
|
688
|
-
}
|
|
689
|
-
const opts = raw.compilerOptions ?? {};
|
|
690
|
-
const baseUrl = opts.baseUrl ? path.resolve(dir, opts.baseUrl) : parentResult.baseUrl;
|
|
691
|
-
const ownEntries = Object.entries(opts.paths ?? {}).map(([pattern, targets]) => ({
|
|
692
|
-
pattern,
|
|
693
|
-
targets: targets,
|
|
694
|
-
configDir: dir,
|
|
695
|
-
}));
|
|
696
|
-
const ownPatterns = new Set(ownEntries.map((e) => e.pattern));
|
|
697
|
-
const parentEntries = parentResult.entries.filter((e) => !ownPatterns.has(e.pattern));
|
|
698
|
-
return { baseUrl, entries: [...ownEntries, ...parentEntries] };
|
|
699
|
-
}
|
|
700
|
-
const { baseUrl, entries } = readConfig(tsconfigPath);
|
|
616
|
+
const absPath = path.resolve(tsconfigPath);
|
|
617
|
+
const configDir = path.dirname(absPath);
|
|
618
|
+
const { config, error } = ts.readConfigFile(absPath, ts.sys.readFile);
|
|
619
|
+
if (error)
|
|
620
|
+
return { name: 'tsconfig-paths' };
|
|
621
|
+
const { options } = ts.parseJsonConfigFileContent(config, ts.sys, configDir);
|
|
622
|
+
const baseUrl = options.baseUrl ?? null;
|
|
623
|
+
const pathsBase = options.pathsBasePath ?? configDir;
|
|
624
|
+
const paths = options.paths ?? {};
|
|
701
625
|
function resolveWithExtensions(base) {
|
|
702
626
|
for (const ext of ['', '.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx']) {
|
|
703
627
|
if (fs.existsSync(base + ext))
|
|
@@ -708,7 +632,7 @@ function tsconfigPathsPlugin(tsconfigPath) {
|
|
|
708
632
|
return {
|
|
709
633
|
name: 'tsconfig-paths',
|
|
710
634
|
async resolveId(source, importer) {
|
|
711
|
-
for (const
|
|
635
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
712
636
|
if (!targets.length)
|
|
713
637
|
continue;
|
|
714
638
|
const isWild = pattern.endsWith('/*');
|
|
@@ -716,7 +640,7 @@ function tsconfigPathsPlugin(tsconfigPath) {
|
|
|
716
640
|
const prefix = pattern.slice(0, -2);
|
|
717
641
|
if (source === prefix || source.startsWith(prefix + '/')) {
|
|
718
642
|
const rest = source.startsWith(prefix + '/') ? source.slice(prefix.length + 1) : '';
|
|
719
|
-
const targetBase = path.resolve(
|
|
643
|
+
const targetBase = path.resolve(pathsBase, targets[0].replace('*', rest));
|
|
720
644
|
const resolved = resolveWithExtensions(targetBase);
|
|
721
645
|
if (resolved) {
|
|
722
646
|
const result = await this.resolve(resolved, importer, { skipSelf: true });
|
|
@@ -726,7 +650,7 @@ function tsconfigPathsPlugin(tsconfigPath) {
|
|
|
726
650
|
}
|
|
727
651
|
}
|
|
728
652
|
else if (source === pattern) {
|
|
729
|
-
const targetBase = path.resolve(
|
|
653
|
+
const targetBase = path.resolve(pathsBase, targets[0]);
|
|
730
654
|
const resolved = resolveWithExtensions(targetBase);
|
|
731
655
|
if (resolved) {
|
|
732
656
|
const result = await this.resolve(resolved, importer, { skipSelf: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steambrew/ttc",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "rollup -c",
|
|
12
|
-
"dev": "rollup -c -w"
|
|
13
|
-
"prepare": "bun run build"
|
|
12
|
+
"dev": "rollup -c -w"
|
|
14
13
|
},
|
|
15
14
|
"publishConfig": {
|
|
16
15
|
"access": "public"
|
|
@@ -32,6 +31,7 @@
|
|
|
32
31
|
"@rollup/plugin-replace": "^6.0.2",
|
|
33
32
|
"@rollup/plugin-terser": "^0.4.4",
|
|
34
33
|
"@rollup/plugin-typescript": "^12.1.2",
|
|
34
|
+
"typescript": ">=5.0.0",
|
|
35
35
|
"@rollup/plugin-url": "^8.0.2",
|
|
36
36
|
"@rollup/pluginutils": "^5.1.4",
|
|
37
37
|
"chalk": "^5.4.1",
|
package/rollup.config.js
CHANGED
|
@@ -2,17 +2,22 @@ import commonjs from '@rollup/plugin-commonjs';
|
|
|
2
2
|
import typescript from '@rollup/plugin-typescript';
|
|
3
3
|
import json from '@rollup/plugin-json';
|
|
4
4
|
import nodeResolve from '@rollup/plugin-node-resolve';
|
|
5
|
+
import replace from '@rollup/plugin-replace';
|
|
5
6
|
import { readFileSync } from 'fs';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
6
8
|
|
|
7
9
|
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
|
8
10
|
|
|
11
|
+
let gitCommit = 'unknown';
|
|
12
|
+
try { gitCommit = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim(); } catch {}
|
|
13
|
+
|
|
9
14
|
export default {
|
|
10
15
|
input: 'src/index.ts',
|
|
11
16
|
context: 'window',
|
|
12
17
|
output: {
|
|
13
18
|
file: 'dist/index.js',
|
|
14
19
|
},
|
|
15
|
-
plugins: [commonjs(), typescript(), json(), nodeResolve()],
|
|
20
|
+
plugins: [replace({ __GIT_COMMIT__: JSON.stringify(gitCommit), preventAssignment: true }), commonjs(), typescript(), json(), nodeResolve()],
|
|
16
21
|
external: [
|
|
17
22
|
...Object.keys(pkg.dependencies || {}),
|
|
18
23
|
...Object.keys(pkg.peerDependencies || {}),
|
package/src/check-health.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import path from 'path';
|
|
2
32
|
import { existsSync, readFile } from 'fs';
|
|
3
33
|
import { Logger } from './logger';
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin / env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ==================================================
|
|
5
|
+
* _____ _ _ _ _
|
|
6
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
7
|
+
* | | | | | | | -_| | | | | | |
|
|
8
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
9
|
+
*
|
|
10
|
+
* ==================================================
|
|
11
|
+
*
|
|
12
|
+
* Copyright (c) 2026 Project Millennium
|
|
13
|
+
*
|
|
14
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
* in the Software without restriction, including without limitation the rights
|
|
17
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
* furnished to do so, subject to the following conditions:
|
|
20
|
+
*
|
|
21
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
* copies or substantial portions of the Software.
|
|
23
|
+
*
|
|
24
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
* SOFTWARE.
|
|
31
|
+
*/
|
|
2
32
|
|
|
3
33
|
/**
|
|
4
34
|
* this component serves as:
|
package/src/logger.ts
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import chalk from 'chalk';
|
|
2
32
|
import { readFileSync } from 'fs';
|
|
3
33
|
import path, { dirname } from 'path';
|
|
4
34
|
import { fileURLToPath } from 'url';
|
|
5
35
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
).version;
|
|
36
|
+
declare const __GIT_COMMIT__: string;
|
|
37
|
+
|
|
38
|
+
const version: string = JSON.parse(readFileSync(path.resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8')).version;
|
|
9
39
|
|
|
10
40
|
interface DoneOptions {
|
|
11
41
|
elapsedMs: number;
|
|
@@ -38,11 +68,11 @@ const Logger = {
|
|
|
38
68
|
|
|
39
69
|
done({ elapsedMs, buildType, sysfsCount, envCount }: DoneOptions) {
|
|
40
70
|
const elapsed = `${(elapsedMs / 1000).toFixed(2)}s`;
|
|
41
|
-
const meta: string[] = [`ttc v${version}`];
|
|
71
|
+
const meta: string[] = [`ttc v${version} (${__GIT_COMMIT__})`];
|
|
42
72
|
if (buildType === 'dev') meta.push('no type checking');
|
|
43
|
-
if (sysfsCount) meta.push(`${sysfsCount}
|
|
73
|
+
if (sysfsCount) meta.push(`${sysfsCount} bundled file${sysfsCount > 1 ? 's' : ''}`);
|
|
44
74
|
if (envCount) meta.push(`${envCount} env var${envCount > 1 ? 's' : ''}`);
|
|
45
|
-
console.log(`${chalk.green('Finished')} ${buildType} in ${elapsed} ` + chalk.dim(
|
|
75
|
+
console.log(`${chalk.green('Finished')} ${buildType} in ${elapsed} ` + chalk.dim(meta.join(', ')));
|
|
46
76
|
},
|
|
47
77
|
|
|
48
78
|
failed({ elapsedMs, buildType }: Pick<DoneOptions, 'elapsedMs' | 'buildType'>) {
|
package/src/plugin-api.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
declare global {
|
|
2
32
|
interface Window {
|
|
3
33
|
/**
|
package/src/plugin-json.d.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
/**
|
|
2
32
|
* generated from https://raw.githubusercontent.com/SteamClientHomebrew/Millennium/main/src/sys/plugin-schema.json
|
|
3
33
|
*/
|
package/src/query-parser.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import { Logger } from './logger';
|
|
2
32
|
|
|
3
33
|
export const PrintParamHelp = () => {
|
package/src/static-embed.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import * as parser from '@babel/parser';
|
|
2
32
|
import { createFilter } from '@rollup/pluginutils';
|
|
3
33
|
import fs from 'fs';
|
|
@@ -47,6 +77,7 @@ export default function constSysfsExpr(options: EmbedPluginOptions = {}): SysfsP
|
|
|
47
77
|
const filter = createFilter(options.include, options.exclude);
|
|
48
78
|
const pluginName = 'millennium-const-sysfs-expr';
|
|
49
79
|
let count = 0;
|
|
80
|
+
const globCache = new Map<string, string>();
|
|
50
81
|
|
|
51
82
|
const plugin: Plugin = {
|
|
52
83
|
name: pluginName,
|
|
@@ -97,9 +128,6 @@ export default function constSysfsExpr(options: EmbedPluginOptions = {}): SysfsP
|
|
|
97
128
|
}
|
|
98
129
|
}
|
|
99
130
|
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
traverse(ast, {
|
|
103
131
|
CallExpression: (nodePath) => {
|
|
104
132
|
const node = nodePath.node;
|
|
105
133
|
if (node.callee.type === 'Identifier' && node.callee.name === 'constSysfsExpr') {
|
|
@@ -220,55 +248,61 @@ export default function constSysfsExpr(options: EmbedPluginOptions = {}): SysfsP
|
|
|
220
248
|
|
|
221
249
|
let embeddedContent: string;
|
|
222
250
|
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
!isPotentialPattern &&
|
|
227
|
-
fs.existsSync(path.resolve(searchBasePath, pathOrPattern)) &&
|
|
228
|
-
fs.statSync(path.resolve(searchBasePath, pathOrPattern)).isFile()
|
|
229
|
-
) {
|
|
230
|
-
const singleFilePath = path.resolve(searchBasePath, pathOrPattern);
|
|
231
|
-
|
|
232
|
-
try {
|
|
233
|
-
const rawContent: string | Buffer = fs.readFileSync(singleFilePath, callOptions.encoding);
|
|
234
|
-
const contentString = rawContent.toString();
|
|
235
|
-
const fileInfo: FileInfo = {
|
|
236
|
-
content: contentString,
|
|
237
|
-
filePath: singleFilePath,
|
|
238
|
-
fileName: path.relative(searchBasePath, singleFilePath),
|
|
239
|
-
};
|
|
240
|
-
embeddedContent = JSON.stringify(fileInfo);
|
|
241
|
-
this.addWatchFile(singleFilePath);
|
|
242
|
-
} catch (fileError: unknown) {
|
|
243
|
-
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
244
|
-
this.error(`Error reading file ${singleFilePath}: ${message}`, node.loc?.start.index);
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
251
|
+
const cacheKey = `${searchBasePath}\0${pathOrPattern}\0${callOptions.encoding}`;
|
|
252
|
+
if (globCache.has(cacheKey)) {
|
|
253
|
+
embeddedContent = globCache.get(cacheKey)!;
|
|
247
254
|
} else {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
const isPotentialPattern = /[?*+!@()[\]{}]/.test(pathOrPattern);
|
|
256
|
+
|
|
257
|
+
if (
|
|
258
|
+
!isPotentialPattern &&
|
|
259
|
+
fs.existsSync(path.resolve(searchBasePath, pathOrPattern)) &&
|
|
260
|
+
fs.statSync(path.resolve(searchBasePath, pathOrPattern)).isFile()
|
|
261
|
+
) {
|
|
262
|
+
const singleFilePath = path.resolve(searchBasePath, pathOrPattern);
|
|
263
|
+
|
|
256
264
|
try {
|
|
257
|
-
const rawContent: string | Buffer = fs.readFileSync(
|
|
265
|
+
const rawContent: string | Buffer = fs.readFileSync(singleFilePath, callOptions.encoding);
|
|
258
266
|
const contentString = rawContent.toString();
|
|
259
|
-
|
|
267
|
+
const fileInfo: FileInfo = {
|
|
260
268
|
content: contentString,
|
|
261
|
-
filePath:
|
|
262
|
-
fileName: path.relative(searchBasePath,
|
|
263
|
-
}
|
|
264
|
-
|
|
269
|
+
filePath: singleFilePath,
|
|
270
|
+
fileName: path.relative(searchBasePath, singleFilePath),
|
|
271
|
+
};
|
|
272
|
+
embeddedContent = JSON.stringify(fileInfo);
|
|
273
|
+
this.addWatchFile(singleFilePath);
|
|
265
274
|
} catch (fileError: unknown) {
|
|
266
275
|
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
267
|
-
this.
|
|
276
|
+
this.error(`Error reading file ${singleFilePath}: ${message}`, node.loc?.start.index);
|
|
277
|
+
return;
|
|
268
278
|
}
|
|
279
|
+
} else {
|
|
280
|
+
const matchingFiles = glob.sync(pathOrPattern, {
|
|
281
|
+
cwd: searchBasePath,
|
|
282
|
+
nodir: true,
|
|
283
|
+
absolute: true,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const fileInfoArray: FileInfo[] = [];
|
|
287
|
+
for (const fullPath of matchingFiles) {
|
|
288
|
+
try {
|
|
289
|
+
const rawContent: string | Buffer = fs.readFileSync(fullPath, callOptions.encoding);
|
|
290
|
+
const contentString = rawContent.toString();
|
|
291
|
+
fileInfoArray.push({
|
|
292
|
+
content: contentString,
|
|
293
|
+
filePath: fullPath,
|
|
294
|
+
fileName: path.relative(searchBasePath, fullPath),
|
|
295
|
+
});
|
|
296
|
+
this.addWatchFile(fullPath);
|
|
297
|
+
} catch (fileError: unknown) {
|
|
298
|
+
let message = String(fileError instanceof Error ? fileError.message : (fileError ?? 'Unknown file read error'));
|
|
299
|
+
this.warn(`Error reading file ${fullPath}: ${message}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
embeddedContent = JSON.stringify(fileInfoArray);
|
|
269
303
|
}
|
|
270
|
-
|
|
271
|
-
}
|
|
304
|
+
globCache.set(cacheKey, embeddedContent);
|
|
305
|
+
} // end cache miss
|
|
272
306
|
|
|
273
307
|
// Replace the call expression with the generated content string
|
|
274
308
|
magicString.overwrite(node.start, node.end, embeddedContent);
|
package/src/transpiler.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import babel from '@rollup/plugin-babel';
|
|
2
32
|
import commonjs from '@rollup/plugin-commonjs';
|
|
3
33
|
import json from '@rollup/plugin-json';
|
|
@@ -6,6 +36,7 @@ import replace from '@rollup/plugin-replace';
|
|
|
6
36
|
import terser from '@rollup/plugin-terser';
|
|
7
37
|
import typescript from '@rollup/plugin-typescript';
|
|
8
38
|
import esbuild from 'rollup-plugin-esbuild';
|
|
39
|
+
import ts from 'typescript';
|
|
9
40
|
import url from '@rollup/plugin-url';
|
|
10
41
|
import nodePolyfills from 'rollup-plugin-polyfill-node';
|
|
11
42
|
import chalk from 'chalk';
|
|
@@ -137,104 +168,21 @@ function stripPluginPrefix(message: string): string {
|
|
|
137
168
|
|
|
138
169
|
class BuildFailedError extends Error {}
|
|
139
170
|
|
|
140
|
-
interface PathEntry {
|
|
141
|
-
pattern: string;
|
|
142
|
-
targets: string[];
|
|
143
|
-
configDir: string;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* tsconfig.json files use JSONC, not regular JSON.
|
|
148
|
-
* JSONC supports comments and trailing commas, while JSON does not.
|
|
149
|
-
* This function sanitizes JSONC into JSON.parse()-able content.
|
|
150
|
-
*
|
|
151
|
-
* @param text input text
|
|
152
|
-
* @returns object
|
|
153
|
-
*/
|
|
154
|
-
function parseJsonc(text: string): any {
|
|
155
|
-
let out = '';
|
|
156
|
-
let i = 0;
|
|
157
|
-
const n = text.length;
|
|
158
|
-
while (i < n) {
|
|
159
|
-
const ch = text[i];
|
|
160
|
-
if (ch === '"') {
|
|
161
|
-
out += ch;
|
|
162
|
-
i++;
|
|
163
|
-
while (i < n) {
|
|
164
|
-
const c = text[i];
|
|
165
|
-
out += c;
|
|
166
|
-
if (c === '\\') {
|
|
167
|
-
i++;
|
|
168
|
-
if (i < n) {
|
|
169
|
-
out += text[i];
|
|
170
|
-
i++;
|
|
171
|
-
}
|
|
172
|
-
} else if (c === '"') {
|
|
173
|
-
i++;
|
|
174
|
-
break;
|
|
175
|
-
} else i++;
|
|
176
|
-
}
|
|
177
|
-
} else if (ch === '/' && i + 1 < n && text[i + 1] === '/') {
|
|
178
|
-
i += 2;
|
|
179
|
-
while (i < n && text[i] !== '\n') i++;
|
|
180
|
-
} else if (ch === '/' && i + 1 < n && text[i + 1] === '*') {
|
|
181
|
-
i += 2;
|
|
182
|
-
while (i < n - 1 && !(text[i] === '*' && text[i + 1] === '/')) i++;
|
|
183
|
-
i += 2;
|
|
184
|
-
} else if (ch === ',') {
|
|
185
|
-
let j = i + 1;
|
|
186
|
-
while (j < n && (text[j] === ' ' || text[j] === '\t' || text[j] === '\n' || text[j] === '\r')) j++;
|
|
187
|
-
if (j < n && (text[j] === '}' || text[j] === ']')) {
|
|
188
|
-
i++;
|
|
189
|
-
} else {
|
|
190
|
-
out += ch;
|
|
191
|
-
i++;
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
out += ch;
|
|
195
|
-
i++;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return JSON.parse(out);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
171
|
function tsconfigPathsPlugin(tsconfigPath: string): InputPluginOption {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let raw: any;
|
|
205
|
-
try {
|
|
206
|
-
raw = parseJsonc(fs.readFileSync(cfgPath, 'utf8'));
|
|
207
|
-
} catch {
|
|
208
|
-
return { baseUrl: null, entries: [] };
|
|
209
|
-
}
|
|
172
|
+
const absPath = path.resolve(tsconfigPath);
|
|
173
|
+
const configDir = path.dirname(absPath);
|
|
210
174
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const ext = raw.extends as string;
|
|
214
|
-
const parentPath = path.resolve(dir, ext.endsWith('.json') ? ext : `${ext}.json`);
|
|
215
|
-
parentResult = readConfig(parentPath);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const opts = raw.compilerOptions ?? {};
|
|
219
|
-
const baseUrl = opts.baseUrl ? path.resolve(dir, opts.baseUrl as string) : parentResult.baseUrl;
|
|
220
|
-
|
|
221
|
-
const ownEntries: PathEntry[] = Object.entries(opts.paths ?? {}).map(([pattern, targets]) => ({
|
|
222
|
-
pattern,
|
|
223
|
-
targets: targets as string[],
|
|
224
|
-
configDir: dir,
|
|
225
|
-
}));
|
|
226
|
-
|
|
227
|
-
const ownPatterns = new Set(ownEntries.map((e) => e.pattern));
|
|
228
|
-
const parentEntries = parentResult.entries.filter((e) => !ownPatterns.has(e.pattern));
|
|
229
|
-
|
|
230
|
-
return { baseUrl, entries: [...ownEntries, ...parentEntries] };
|
|
231
|
-
}
|
|
175
|
+
const { config, error } = ts.readConfigFile(absPath, ts.sys.readFile);
|
|
176
|
+
if (error) return { name: 'tsconfig-paths' };
|
|
232
177
|
|
|
233
|
-
const {
|
|
178
|
+
const { options } = ts.parseJsonConfigFileContent(config, ts.sys, configDir);
|
|
179
|
+
const baseUrl = options.baseUrl ?? null;
|
|
180
|
+
const pathsBase = (options.pathsBasePath as string | undefined) ?? configDir;
|
|
181
|
+
const paths = options.paths ?? {};
|
|
234
182
|
|
|
235
183
|
function resolveWithExtensions(base: string): string | null {
|
|
236
184
|
for (const ext of ['', '.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx']) {
|
|
237
|
-
|
|
185
|
+
if (fs.existsSync(base + ext) && fs.statSync(base + ext).isFile()) return base + ext;
|
|
238
186
|
}
|
|
239
187
|
return null;
|
|
240
188
|
}
|
|
@@ -242,14 +190,14 @@ function tsconfigPathsPlugin(tsconfigPath: string): InputPluginOption {
|
|
|
242
190
|
return {
|
|
243
191
|
name: 'tsconfig-paths',
|
|
244
192
|
async resolveId(source: string, importer: string | undefined) {
|
|
245
|
-
for (const
|
|
193
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
246
194
|
if (!targets.length) continue;
|
|
247
195
|
const isWild = pattern.endsWith('/*');
|
|
248
196
|
if (isWild) {
|
|
249
197
|
const prefix = pattern.slice(0, -2);
|
|
250
198
|
if (source === prefix || source.startsWith(prefix + '/')) {
|
|
251
199
|
const rest = source.startsWith(prefix + '/') ? source.slice(prefix.length + 1) : '';
|
|
252
|
-
const targetBase = path.resolve(
|
|
200
|
+
const targetBase = path.resolve(pathsBase, targets[0].replace('*', rest));
|
|
253
201
|
const resolved = resolveWithExtensions(targetBase);
|
|
254
202
|
if (resolved) {
|
|
255
203
|
const result = await this.resolve(resolved, importer, { skipSelf: true });
|
|
@@ -257,7 +205,7 @@ function tsconfigPathsPlugin(tsconfigPath: string): InputPluginOption {
|
|
|
257
205
|
}
|
|
258
206
|
}
|
|
259
207
|
} else if (source === pattern) {
|
|
260
|
-
const targetBase = path.resolve(
|
|
208
|
+
const targetBase = path.resolve(pathsBase, targets[0]);
|
|
261
209
|
const resolved = resolveWithExtensions(targetBase);
|
|
262
210
|
if (resolved) {
|
|
263
211
|
const result = await this.resolve(resolved, importer, { skipSelf: true });
|
package/src/version-control.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ==================================================
|
|
3
|
+
* _____ _ _ _ _
|
|
4
|
+
* | |_| | |___ ___ ___|_|_ _ _____
|
|
5
|
+
* | | | | | | | -_| | | | | | |
|
|
6
|
+
* |_|_|_|_|_|_|___|_|_|_|_|_|___|_|_|_|
|
|
7
|
+
*
|
|
8
|
+
* ==================================================
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2026 Project Millennium
|
|
11
|
+
*
|
|
12
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
* in the Software without restriction, including without limitation the rights
|
|
15
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
* furnished to do so, subject to the following conditions:
|
|
18
|
+
*
|
|
19
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
* copies or substantial portions of the Software.
|
|
21
|
+
*
|
|
22
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
* SOFTWARE.
|
|
29
|
+
*/
|
|
30
|
+
|
|
1
31
|
import path from 'path';
|
|
2
32
|
import { fileURLToPath } from 'url';
|
|
3
33
|
import { readFile, access } from 'fs/promises';
|