@ts-ioc-container/bundler 0.0.0
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/AGENTS.md +40 -0
- package/README.md +335 -0
- package/cjm/ImportPaths.js +134 -0
- package/cjm/bin.js +5 -0
- package/cjm/build.js +90 -0
- package/cjm/cli.js +82 -0
- package/cjm/config.js +118 -0
- package/cjm/emit.js +33 -0
- package/cjm/errors.js +15 -0
- package/cjm/exportPredicate.js +13 -0
- package/cjm/glob.js +27 -0
- package/cjm/inclusion.js +24 -0
- package/cjm/index.js +36 -0
- package/cjm/predicateFile.js +30 -0
- package/cjm/render.js +15 -0
- package/cjm/scan.js +144 -0
- package/cjm/utils.js +2 -0
- package/lib/ImportPaths.ts +127 -0
- package/lib/bin.ts +4 -0
- package/lib/build.ts +157 -0
- package/lib/cli.ts +88 -0
- package/lib/config.ts +206 -0
- package/lib/emit.ts +47 -0
- package/lib/errors.ts +14 -0
- package/lib/exportPredicate.ts +45 -0
- package/lib/glob.ts +24 -0
- package/lib/inclusion.ts +61 -0
- package/lib/index.ts +40 -0
- package/lib/predicateFile.ts +36 -0
- package/lib/render.ts +19 -0
- package/lib/scan.ts +133 -0
- package/lib/utils.ts +2 -0
- package/package.json +84 -0
- package/tic.schema.json +134 -0
- package/tpl/index.cjs +81 -0
- package/typings/ImportPaths.d.ts +37 -0
- package/typings/ImportPaths.d.ts.map +1 -0
- package/typings/bin.d.ts +3 -0
- package/typings/bin.d.ts.map +1 -0
- package/typings/build.d.ts +39 -0
- package/typings/build.d.ts.map +1 -0
- package/typings/cli.d.ts +8 -0
- package/typings/cli.d.ts.map +1 -0
- package/typings/config.d.ts +95 -0
- package/typings/config.d.ts.map +1 -0
- package/typings/emit.d.ts +18 -0
- package/typings/emit.d.ts.map +1 -0
- package/typings/errors.d.ts +13 -0
- package/typings/errors.d.ts.map +1 -0
- package/typings/exportPredicate.d.ts +36 -0
- package/typings/exportPredicate.d.ts.map +1 -0
- package/typings/glob.d.ts +7 -0
- package/typings/glob.d.ts.map +1 -0
- package/typings/inclusion.d.ts +42 -0
- package/typings/inclusion.d.ts.map +1 -0
- package/typings/index.d.ts +12 -0
- package/typings/index.d.ts.map +1 -0
- package/typings/predicateFile.d.ts +16 -0
- package/typings/predicateFile.d.ts.map +1 -0
- package/typings/render.d.ts +8 -0
- package/typings/render.d.ts.map +1 -0
- package/typings/scan.d.ts +22 -0
- package/typings/scan.d.ts.map +1 -0
- package/typings/utils.d.ts +3 -0
- package/typings/utils.d.ts.map +1 -0
package/cjm/cli.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.run = run;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const build_1 = require("./build");
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const USAGE = [
|
|
12
|
+
'Usage: ts-ioc-container <command> [options]',
|
|
13
|
+
' tic is a shortcut for ts-ioc-container',
|
|
14
|
+
'',
|
|
15
|
+
'Commands:',
|
|
16
|
+
' ts-ioc-container build [--config <path>] [--check] generate the bundles described by tic.config.json',
|
|
17
|
+
'',
|
|
18
|
+
'Options:',
|
|
19
|
+
' -c, --config <path> config file (default: tic.config.json)',
|
|
20
|
+
' --check write nothing; exit 1 when a generated bundle is out of date',
|
|
21
|
+
' -h, --help show this help',
|
|
22
|
+
' -v, --version show the version',
|
|
23
|
+
].join('\n');
|
|
24
|
+
const VERB = { written: 'wrote', unchanged: 'unchanged', stale: 'stale' };
|
|
25
|
+
const plural = (n) => `${n} registration${n === 1 ? '' : 's'}`;
|
|
26
|
+
function version() {
|
|
27
|
+
return JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(__dirname, '..', 'package.json'), 'utf8')).version;
|
|
28
|
+
}
|
|
29
|
+
class UsageError extends errors_1.TicError {
|
|
30
|
+
}
|
|
31
|
+
function parseBuildArgs(args) {
|
|
32
|
+
const options = { check: false };
|
|
33
|
+
for (let i = 0; i < args.length; i++) {
|
|
34
|
+
const arg = args[i];
|
|
35
|
+
if (arg === '--check')
|
|
36
|
+
options.check = true;
|
|
37
|
+
else if (arg === '--config' || arg === '-c') {
|
|
38
|
+
options.config = args[++i];
|
|
39
|
+
if (!options.config)
|
|
40
|
+
throw new UsageError(`${arg} needs a path`);
|
|
41
|
+
}
|
|
42
|
+
else if (arg.startsWith('--config='))
|
|
43
|
+
options.config = arg.slice('--config='.length);
|
|
44
|
+
else
|
|
45
|
+
throw new UsageError(`unknown option "${arg}"`);
|
|
46
|
+
}
|
|
47
|
+
return options;
|
|
48
|
+
}
|
|
49
|
+
function run(argv, io = { cwd: process.cwd(), stdout: console.log, stderr: console.error }) {
|
|
50
|
+
const [command, ...rest] = argv;
|
|
51
|
+
if (command === '--help' || command === '-h' || command === 'help') {
|
|
52
|
+
io.stdout(USAGE);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
if (command === '--version' || command === '-v') {
|
|
56
|
+
io.stdout(version());
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
if (command !== 'build') {
|
|
61
|
+
io.stderr(command ? `tic: unknown command "${command}"` : USAGE);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
const { config, check } = parseBuildArgs(rest);
|
|
65
|
+
const { outputs } = (0, build_1.build)({ config, check, cwd: io.cwd });
|
|
66
|
+
for (const { status, file, registrations } of outputs) {
|
|
67
|
+
io.stdout(`${VERB[status].padEnd(10)}${node_path_1.default.relative(io.cwd, file)} (${plural(registrations)})`);
|
|
68
|
+
}
|
|
69
|
+
const stale = outputs.filter((o) => o.status === 'stale').length;
|
|
70
|
+
if (stale > 0) {
|
|
71
|
+
io.stderr(`tic: ${stale} generated bundle${stale === 1 ? ' is' : 's are'} out of date — run \`tic build\``);
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
if (!(e instanceof errors_1.TicError))
|
|
78
|
+
throw e;
|
|
79
|
+
io.stderr(`tic: ${e.message}`);
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
package/cjm/config.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_EXCLUDE = exports.DEFAULT_BUNDLE_NAME = exports.DEFAULT_CONFIG_FILE = void 0;
|
|
7
|
+
exports.resolveConfig = resolveConfig;
|
|
8
|
+
exports.loadConfig = loadConfig;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
const glob_1 = require("./glob");
|
|
13
|
+
exports.DEFAULT_CONFIG_FILE = 'tic.config.json';
|
|
14
|
+
exports.DEFAULT_BUNDLE_NAME = 'Bundle';
|
|
15
|
+
exports.DEFAULT_EXCLUDE = [
|
|
16
|
+
'**/*.spec.ts',
|
|
17
|
+
'**/*.test.ts',
|
|
18
|
+
'**/*.spec.tsx',
|
|
19
|
+
'**/*.test.tsx',
|
|
20
|
+
'**/__tests__/**',
|
|
21
|
+
'**/node_modules/**',
|
|
22
|
+
];
|
|
23
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
const isNonEmptyString = (value) => typeof value === 'string' && value.length > 0;
|
|
25
|
+
const isStringArray = (value) => Array.isArray(value) && value.every(isNonEmptyString);
|
|
26
|
+
const isIdentifier = (value) => typeof value === 'string' && /^[A-Za-z_$][\w$]*$/.test(value);
|
|
27
|
+
const fail = (field, expected) => {
|
|
28
|
+
throw new errors_1.TicConfigError(`${field}: expected ${expected}`);
|
|
29
|
+
};
|
|
30
|
+
function toNamespace(value, field) {
|
|
31
|
+
if (isNonEmptyString(value))
|
|
32
|
+
return { path: value, recursive: true };
|
|
33
|
+
if (!isObject(value))
|
|
34
|
+
return fail(field, 'a string or an object with "path"');
|
|
35
|
+
if (!isNonEmptyString(value.path))
|
|
36
|
+
return fail(`${field}.path`, 'a non-empty string');
|
|
37
|
+
if (value.recursive !== undefined && typeof value.recursive !== 'boolean')
|
|
38
|
+
return fail(`${field}.recursive`, 'a boolean');
|
|
39
|
+
return { path: value.path, recursive: value.recursive ?? true };
|
|
40
|
+
}
|
|
41
|
+
const SELECTOR_FIELDS = new Set(['export', 'decorators', 'nameGlob']);
|
|
42
|
+
function toSelector(value, field) {
|
|
43
|
+
if (value === undefined)
|
|
44
|
+
return { export: 'any' };
|
|
45
|
+
if (!isObject(value))
|
|
46
|
+
return fail(field, 'an object');
|
|
47
|
+
const unknown = Object.keys(value).find((key) => !SELECTOR_FIELDS.has(key));
|
|
48
|
+
if (unknown)
|
|
49
|
+
throw new errors_1.TicConfigError(`${field}.${unknown}: unknown field`);
|
|
50
|
+
const { export: kind, decorators, nameGlob } = value;
|
|
51
|
+
if (kind !== undefined && kind !== 'any' && kind !== 'named' && kind !== 'default') {
|
|
52
|
+
return fail(`${field}.export`, '"any", "named" or "default"');
|
|
53
|
+
}
|
|
54
|
+
if (decorators !== undefined && !(isStringArray(decorators) && decorators.length > 0)) {
|
|
55
|
+
return fail(`${field}.decorators`, 'a non-empty array of strings');
|
|
56
|
+
}
|
|
57
|
+
if (nameGlob !== undefined && !isNonEmptyString(nameGlob))
|
|
58
|
+
return fail(`${field}.nameGlob`, 'a non-empty string');
|
|
59
|
+
return { export: kind ?? 'any', decorators, nameGlob: nameGlob === undefined ? undefined : (0, glob_1.globToRegExp)(nameGlob) };
|
|
60
|
+
}
|
|
61
|
+
function toBundle(value, field, dir) {
|
|
62
|
+
if (!isObject(value))
|
|
63
|
+
return fail(field, 'an object');
|
|
64
|
+
const { output, name, namespaces, select, exclude, include, filterExports } = value;
|
|
65
|
+
if (!isNonEmptyString(output))
|
|
66
|
+
return fail(`${field}.output`, 'a non-empty string');
|
|
67
|
+
if (name !== undefined && !isIdentifier(name))
|
|
68
|
+
return fail(`${field}.name`, 'a valid identifier');
|
|
69
|
+
if (!Array.isArray(namespaces) || namespaces.length === 0)
|
|
70
|
+
return fail(`${field}.namespaces`, 'a non-empty array');
|
|
71
|
+
if (exclude !== undefined && !isStringArray(exclude))
|
|
72
|
+
return fail(`${field}.exclude`, 'an array of strings');
|
|
73
|
+
if (include !== undefined && !isNonEmptyString(include))
|
|
74
|
+
return fail(`${field}.include`, 'a non-empty string');
|
|
75
|
+
if (filterExports !== undefined && !isNonEmptyString(filterExports)) {
|
|
76
|
+
return fail(`${field}.filterExports`, 'a non-empty string');
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
output: node_path_1.default.resolve(dir, output),
|
|
80
|
+
name: name ?? exports.DEFAULT_BUNDLE_NAME,
|
|
81
|
+
namespaces: namespaces.map((ns, i) => toNamespace(ns, `${field}.namespaces[${i}]`)),
|
|
82
|
+
select: toSelector(select, `${field}.select`),
|
|
83
|
+
exclude: exclude ?? exports.DEFAULT_EXCLUDE,
|
|
84
|
+
include: include === undefined ? undefined : node_path_1.default.resolve(dir, include),
|
|
85
|
+
filterExports: filterExports === undefined ? undefined : node_path_1.default.resolve(dir, filterExports),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function resolveConfig(content, file) {
|
|
89
|
+
const dir = node_path_1.default.dirname(file);
|
|
90
|
+
if (!isObject(content))
|
|
91
|
+
return fail('config', 'a JSON object');
|
|
92
|
+
const { tsconfig, importExtension, bundles } = content;
|
|
93
|
+
if (tsconfig !== undefined && !isNonEmptyString(tsconfig))
|
|
94
|
+
return fail('tsconfig', 'a non-empty string');
|
|
95
|
+
if (importExtension !== undefined && typeof importExtension !== 'string')
|
|
96
|
+
return fail('importExtension', 'a string');
|
|
97
|
+
if (!Array.isArray(bundles) || bundles.length === 0)
|
|
98
|
+
return fail('bundles', 'a non-empty array');
|
|
99
|
+
return {
|
|
100
|
+
file,
|
|
101
|
+
dir,
|
|
102
|
+
tsconfig: { file: node_path_1.default.resolve(dir, tsconfig ?? 'tsconfig.json'), required: tsconfig !== undefined },
|
|
103
|
+
importExtension,
|
|
104
|
+
bundles: bundles.map((m, i) => toBundle(m, `bundles[${i}]`, dir)),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function loadConfig(file) {
|
|
108
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
109
|
+
throw new errors_1.TicConfigError(`config file not found: ${file}`);
|
|
110
|
+
let content;
|
|
111
|
+
try {
|
|
112
|
+
content = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
throw new errors_1.TicConfigError(`${file} is not valid JSON: ${e.message}`);
|
|
116
|
+
}
|
|
117
|
+
return resolveConfig(content, file);
|
|
118
|
+
}
|
package/cjm/emit.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emitBundle = emitBundle;
|
|
4
|
+
const render_1 = require("./render");
|
|
5
|
+
const RUNTIME_IMPORTS = ['IContainer', 'IContainerModule', 'IRegistration', 'Registration'];
|
|
6
|
+
function emitBundle({ name, configPath, namespaces, classes }) {
|
|
7
|
+
const taken = new Set([...RUNTIME_IMPORTS, 'registrations', name]);
|
|
8
|
+
const uniqueName = (wanted) => {
|
|
9
|
+
let candidate = wanted;
|
|
10
|
+
for (let n = 2; taken.has(candidate); n++)
|
|
11
|
+
candidate = `${wanted}_${n}`;
|
|
12
|
+
taken.add(candidate);
|
|
13
|
+
return candidate;
|
|
14
|
+
};
|
|
15
|
+
const byFile = new Map();
|
|
16
|
+
const bindings = classes.map((cls) => {
|
|
17
|
+
const binding = uniqueName(cls.localName);
|
|
18
|
+
const entry = byFile.get(cls.file) ?? { specifier: cls.specifier, named: [] };
|
|
19
|
+
byFile.set(cls.file, entry);
|
|
20
|
+
if (cls.exportName === 'default')
|
|
21
|
+
entry.defaultName = binding;
|
|
22
|
+
else
|
|
23
|
+
entry.named.push(binding === cls.exportName ? binding : `${cls.exportName} as ${binding}`);
|
|
24
|
+
return binding;
|
|
25
|
+
});
|
|
26
|
+
return (0, render_1.renderProtocol)('Bundle.ts.hbs', {
|
|
27
|
+
configPath,
|
|
28
|
+
namespaces,
|
|
29
|
+
name,
|
|
30
|
+
imports: [...byFile.values()],
|
|
31
|
+
registrations: bindings,
|
|
32
|
+
});
|
|
33
|
+
}
|
package/cjm/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NamespaceNotFoundError = exports.TicConfigError = exports.TicError = void 0;
|
|
4
|
+
class TicError extends Error {
|
|
5
|
+
name = 'TicError';
|
|
6
|
+
}
|
|
7
|
+
exports.TicError = TicError;
|
|
8
|
+
class TicConfigError extends TicError {
|
|
9
|
+
name = 'TicConfigError';
|
|
10
|
+
}
|
|
11
|
+
exports.TicConfigError = TicConfigError;
|
|
12
|
+
class NamespaceNotFoundError extends TicError {
|
|
13
|
+
name = 'NamespaceNotFoundError';
|
|
14
|
+
}
|
|
15
|
+
exports.NamespaceNotFoundError = NamespaceNotFoundError;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EXPORTS_CONVENTION = void 0;
|
|
4
|
+
exports.findConventionalExportPredicate = findConventionalExportPredicate;
|
|
5
|
+
exports.loadExportPredicate = loadExportPredicate;
|
|
6
|
+
const predicateFile_1 = require("./predicateFile");
|
|
7
|
+
exports.EXPORTS_CONVENTION = (0, predicateFile_1.conventionalNames)('tic.exports');
|
|
8
|
+
function findConventionalExportPredicate(dir) {
|
|
9
|
+
return (0, predicateFile_1.findConventionalFile)(dir, 'tic.exports');
|
|
10
|
+
}
|
|
11
|
+
function loadExportPredicate(file) {
|
|
12
|
+
return (0, predicateFile_1.loadPredicateFile)(file, 'ExportPredicate');
|
|
13
|
+
}
|
package/cjm/glob.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toPosix = void 0;
|
|
4
|
+
exports.globToRegExp = globToRegExp;
|
|
5
|
+
function globToRegExp(glob) {
|
|
6
|
+
let source = '';
|
|
7
|
+
for (let i = 0; i < glob.length; i++) {
|
|
8
|
+
const char = glob[i];
|
|
9
|
+
if (char === '*' && glob[i + 1] === '*') {
|
|
10
|
+
const slash = glob[i + 2] === '/';
|
|
11
|
+
source += slash ? '(?:.*/)?' : '.*';
|
|
12
|
+
i += slash ? 2 : 1;
|
|
13
|
+
}
|
|
14
|
+
else if (char === '*') {
|
|
15
|
+
source += '[^/]*';
|
|
16
|
+
}
|
|
17
|
+
else if (char === '?') {
|
|
18
|
+
source += '[^/]';
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
source += char.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return new RegExp(`^${source}$`);
|
|
25
|
+
}
|
|
26
|
+
const toPosix = (file) => file.split('\\').join('/');
|
|
27
|
+
exports.toPosix = toPosix;
|
package/cjm/inclusion.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INCLUSION_CONVENTION = void 0;
|
|
4
|
+
exports.fileTags = fileTags;
|
|
5
|
+
exports.byTags = byTags;
|
|
6
|
+
exports.findConventionalPredicate = findConventionalPredicate;
|
|
7
|
+
exports.loadInclusionPredicate = loadInclusionPredicate;
|
|
8
|
+
const predicateFile_1 = require("./predicateFile");
|
|
9
|
+
function fileTags(filename) {
|
|
10
|
+
return filename
|
|
11
|
+
.slice(filename.lastIndexOf('/') + 1)
|
|
12
|
+
.split('.')
|
|
13
|
+
.slice(1, -1);
|
|
14
|
+
}
|
|
15
|
+
function byTags(predicate) {
|
|
16
|
+
return (context) => predicate(fileTags(context.filename), context);
|
|
17
|
+
}
|
|
18
|
+
exports.INCLUSION_CONVENTION = (0, predicateFile_1.conventionalNames)('tic.include');
|
|
19
|
+
function findConventionalPredicate(dir) {
|
|
20
|
+
return (0, predicateFile_1.findConventionalFile)(dir, 'tic.include');
|
|
21
|
+
}
|
|
22
|
+
function loadInclusionPredicate(file) {
|
|
23
|
+
return (0, predicateFile_1.loadPredicateFile)(file, 'InclusionPredicate');
|
|
24
|
+
}
|
package/cjm/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NamespaceNotFoundError = exports.TicConfigError = exports.TicError = exports.globToRegExp = exports.emitBundle = exports.listSourceFiles = exports.findClasses = exports.loadInclusionPredicate = exports.findConventionalPredicate = exports.INCLUSION_CONVENTION = exports.fileTags = exports.byTags = exports.loadExportPredicate = exports.findConventionalExportPredicate = exports.EXPORTS_CONVENTION = exports.ImportPaths = exports.DEFAULT_BUNDLE_NAME = exports.DEFAULT_EXCLUDE = exports.DEFAULT_CONFIG_FILE = exports.resolveConfig = exports.loadConfig = exports.run = exports.build = void 0;
|
|
4
|
+
var build_1 = require("./build");
|
|
5
|
+
Object.defineProperty(exports, "build", { enumerable: true, get: function () { return build_1.build; } });
|
|
6
|
+
var cli_1 = require("./cli");
|
|
7
|
+
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return cli_1.run; } });
|
|
8
|
+
var config_1 = require("./config");
|
|
9
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
|
|
10
|
+
Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return config_1.resolveConfig; } });
|
|
11
|
+
Object.defineProperty(exports, "DEFAULT_CONFIG_FILE", { enumerable: true, get: function () { return config_1.DEFAULT_CONFIG_FILE; } });
|
|
12
|
+
Object.defineProperty(exports, "DEFAULT_EXCLUDE", { enumerable: true, get: function () { return config_1.DEFAULT_EXCLUDE; } });
|
|
13
|
+
Object.defineProperty(exports, "DEFAULT_BUNDLE_NAME", { enumerable: true, get: function () { return config_1.DEFAULT_BUNDLE_NAME; } });
|
|
14
|
+
var ImportPaths_1 = require("./ImportPaths");
|
|
15
|
+
Object.defineProperty(exports, "ImportPaths", { enumerable: true, get: function () { return ImportPaths_1.ImportPaths; } });
|
|
16
|
+
var exportPredicate_1 = require("./exportPredicate");
|
|
17
|
+
Object.defineProperty(exports, "EXPORTS_CONVENTION", { enumerable: true, get: function () { return exportPredicate_1.EXPORTS_CONVENTION; } });
|
|
18
|
+
Object.defineProperty(exports, "findConventionalExportPredicate", { enumerable: true, get: function () { return exportPredicate_1.findConventionalExportPredicate; } });
|
|
19
|
+
Object.defineProperty(exports, "loadExportPredicate", { enumerable: true, get: function () { return exportPredicate_1.loadExportPredicate; } });
|
|
20
|
+
var inclusion_1 = require("./inclusion");
|
|
21
|
+
Object.defineProperty(exports, "byTags", { enumerable: true, get: function () { return inclusion_1.byTags; } });
|
|
22
|
+
Object.defineProperty(exports, "fileTags", { enumerable: true, get: function () { return inclusion_1.fileTags; } });
|
|
23
|
+
Object.defineProperty(exports, "INCLUSION_CONVENTION", { enumerable: true, get: function () { return inclusion_1.INCLUSION_CONVENTION; } });
|
|
24
|
+
Object.defineProperty(exports, "findConventionalPredicate", { enumerable: true, get: function () { return inclusion_1.findConventionalPredicate; } });
|
|
25
|
+
Object.defineProperty(exports, "loadInclusionPredicate", { enumerable: true, get: function () { return inclusion_1.loadInclusionPredicate; } });
|
|
26
|
+
var scan_1 = require("./scan");
|
|
27
|
+
Object.defineProperty(exports, "findClasses", { enumerable: true, get: function () { return scan_1.findClasses; } });
|
|
28
|
+
Object.defineProperty(exports, "listSourceFiles", { enumerable: true, get: function () { return scan_1.listSourceFiles; } });
|
|
29
|
+
var emit_1 = require("./emit");
|
|
30
|
+
Object.defineProperty(exports, "emitBundle", { enumerable: true, get: function () { return emit_1.emitBundle; } });
|
|
31
|
+
var glob_1 = require("./glob");
|
|
32
|
+
Object.defineProperty(exports, "globToRegExp", { enumerable: true, get: function () { return glob_1.globToRegExp; } });
|
|
33
|
+
var errors_1 = require("./errors");
|
|
34
|
+
Object.defineProperty(exports, "TicError", { enumerable: true, get: function () { return errors_1.TicError; } });
|
|
35
|
+
Object.defineProperty(exports, "TicConfigError", { enumerable: true, get: function () { return errors_1.TicConfigError; } });
|
|
36
|
+
Object.defineProperty(exports, "NamespaceNotFoundError", { enumerable: true, get: function () { return errors_1.NamespaceNotFoundError; } });
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.conventionalNames = void 0;
|
|
7
|
+
exports.findConventionalFile = findConventionalFile;
|
|
8
|
+
exports.loadPredicateFile = loadPredicateFile;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_module_1 = require("node:module");
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const errors_1 = require("./errors");
|
|
13
|
+
const EXTENSIONS = ['cjs', 'js', 'mjs', 'ts', 'cts', 'mts'];
|
|
14
|
+
const conventionalNames = (base) => EXTENSIONS.map((ext) => `${base}.${ext}`);
|
|
15
|
+
exports.conventionalNames = conventionalNames;
|
|
16
|
+
function findConventionalFile(dir, base) {
|
|
17
|
+
return (0, exports.conventionalNames)(base)
|
|
18
|
+
.map((name) => node_path_1.default.join(dir, name))
|
|
19
|
+
.find((file) => (0, node_fs_1.existsSync)(file));
|
|
20
|
+
}
|
|
21
|
+
function loadPredicateFile(file, typeName) {
|
|
22
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
23
|
+
throw new errors_1.TicConfigError(`predicate file not found: ${file}`);
|
|
24
|
+
const loaded = (0, node_module_1.createRequire)(file)(file);
|
|
25
|
+
const predicate = typeof loaded === 'function' ? loaded : loaded?.default;
|
|
26
|
+
if (typeof predicate !== 'function') {
|
|
27
|
+
throw new errors_1.TicConfigError(`${file} must export an ${typeName} function (default export or module.exports)`);
|
|
28
|
+
}
|
|
29
|
+
return predicate;
|
|
30
|
+
}
|
package/cjm/render.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderProtocol = renderProtocol;
|
|
7
|
+
require("../tpl/index.cjs");
|
|
8
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
9
|
+
handlebars_1.default.registerHelper('join', (items, separator) => items.join(separator));
|
|
10
|
+
function renderProtocol(name, data) {
|
|
11
|
+
const protocol = handlebars_1.default.templates?.[name];
|
|
12
|
+
if (!protocol)
|
|
13
|
+
throw new Error(`Protocol not found: ${name}`);
|
|
14
|
+
return protocol(data);
|
|
15
|
+
}
|
package/cjm/scan.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.listSourceFiles = listSourceFiles;
|
|
40
|
+
exports.findClasses = findClasses;
|
|
41
|
+
const node_fs_1 = require("node:fs");
|
|
42
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
|
+
const ts = __importStar(require("typescript"));
|
|
44
|
+
const SOURCE_FILE = /\.(tsx?|mts|cts)$/;
|
|
45
|
+
const DECLARATION_FILE = /\.d\.[mc]?tsx?$/;
|
|
46
|
+
function listSourceFiles(dir, recursive, isExcluded) {
|
|
47
|
+
const files = [];
|
|
48
|
+
const walk = (current) => {
|
|
49
|
+
for (const entry of (0, node_fs_1.readdirSync)(current, { withFileTypes: true })) {
|
|
50
|
+
const full = node_path_1.default.join(current, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
if (recursive && entry.name !== 'node_modules')
|
|
53
|
+
walk(full);
|
|
54
|
+
}
|
|
55
|
+
else if (SOURCE_FILE.test(entry.name) && !DECLARATION_FILE.test(entry.name) && !isExcluded(full)) {
|
|
56
|
+
files.push(full);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
walk(dir);
|
|
61
|
+
return files.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
62
|
+
}
|
|
63
|
+
const hasModifier = (node, kind) => ts.canHaveModifiers(node) && (ts.getModifiers(node) ?? []).some((m) => m.kind === kind);
|
|
64
|
+
function decoratorName(decorator, imports) {
|
|
65
|
+
const callee = ts.isCallExpression(decorator.expression) ? decorator.expression.expression : decorator.expression;
|
|
66
|
+
if (ts.isIdentifier(callee))
|
|
67
|
+
return imports.get(callee.text) ?? callee.text;
|
|
68
|
+
if (ts.isPropertyAccessExpression(callee))
|
|
69
|
+
return callee.name.text;
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function nameFromFile(file) {
|
|
73
|
+
const words = node_path_1.default
|
|
74
|
+
.basename(file)
|
|
75
|
+
.split('.')[0]
|
|
76
|
+
.split(/[^A-Za-z0-9_$]+/)
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
const name = words.map((w) => w[0].toUpperCase() + w.slice(1)).join('');
|
|
79
|
+
return /^[A-Za-z_$]/.test(name) ? name : `_${name}`;
|
|
80
|
+
}
|
|
81
|
+
function findClasses(file, selector, text = (0, node_fs_1.readFileSync)(file, 'utf8')) {
|
|
82
|
+
const kind = file.endsWith('x') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
|
83
|
+
const source = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, false, kind);
|
|
84
|
+
const imports = new Map();
|
|
85
|
+
const classes = new Map();
|
|
86
|
+
const exported = [];
|
|
87
|
+
for (const statement of source.statements) {
|
|
88
|
+
if (ts.isImportDeclaration(statement)) {
|
|
89
|
+
const bindings = statement.importClause?.namedBindings;
|
|
90
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
91
|
+
for (const el of bindings.elements)
|
|
92
|
+
imports.set(el.name.text, (el.propertyName ?? el.name).text);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (ts.isClassDeclaration(statement)) {
|
|
96
|
+
if (statement.name)
|
|
97
|
+
classes.set(statement.name.text, statement);
|
|
98
|
+
if (hasModifier(statement, ts.SyntaxKind.ExportKeyword)) {
|
|
99
|
+
const isDefault = hasModifier(statement, ts.SyntaxKind.DefaultKeyword);
|
|
100
|
+
exported.push({ node: statement, exportName: isDefault ? 'default' : statement.name.text });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (ts.isExportDeclaration(statement) && !statement.moduleSpecifier && !statement.isTypeOnly) {
|
|
104
|
+
const clause = statement.exportClause;
|
|
105
|
+
if (clause && ts.isNamedExports(clause)) {
|
|
106
|
+
for (const el of clause.elements) {
|
|
107
|
+
if (!el.isTypeOnly)
|
|
108
|
+
exported.push({ node: undefined, local: (el.propertyName ?? el.name).text, exportName: el.name.text });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (ts.isExportAssignment(statement) && !statement.isExportEquals && ts.isIdentifier(statement.expression)) {
|
|
113
|
+
exported.push({ node: undefined, local: statement.expression.text, exportName: 'default' });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const decoratorsOf = (node) => (ts.getDecorators(node) ?? []).map((d) => decoratorName(d, imports)).filter((n) => !!n);
|
|
117
|
+
return exported
|
|
118
|
+
.map(({ node, local, exportName }) => ({ node: node ?? (local ? classes.get(local) : undefined), exportName }))
|
|
119
|
+
.filter((e) => !!e.node)
|
|
120
|
+
.map(({ node, exportName }) => ({
|
|
121
|
+
node,
|
|
122
|
+
exportName,
|
|
123
|
+
className: node.name?.text ?? nameFromFile(file),
|
|
124
|
+
decorators: decoratorsOf(node),
|
|
125
|
+
}))
|
|
126
|
+
.filter(({ node, exportName, className, decorators }) => {
|
|
127
|
+
if (hasModifier(node, ts.SyntaxKind.AbstractKeyword))
|
|
128
|
+
return false;
|
|
129
|
+
if (selector.export !== 'any' && (exportName === 'default') !== (selector.export === 'default'))
|
|
130
|
+
return false;
|
|
131
|
+
if (selector.decorators && !selector.decorators.some((name) => decorators.includes(name)))
|
|
132
|
+
return false;
|
|
133
|
+
return !selector.nameGlob || selector.nameGlob.test(className);
|
|
134
|
+
})
|
|
135
|
+
.sort((a, b) => a.node.pos - b.node.pos)
|
|
136
|
+
.map(({ exportName, className, decorators }) => ({
|
|
137
|
+
file,
|
|
138
|
+
exportName,
|
|
139
|
+
localName: exportName === 'default' ? className : exportName,
|
|
140
|
+
className,
|
|
141
|
+
isDefault: exportName === 'default',
|
|
142
|
+
decorators,
|
|
143
|
+
}));
|
|
144
|
+
}
|
package/cjm/utils.js
ADDED