@knighted/css 1.0.0-rc.1 → 1.0.0-rc.10
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/bin/generate-types.js +31 -0
- package/dist/cjs/css.cjs +100 -22
- package/dist/cjs/css.d.cts +5 -6
- package/dist/cjs/generateTypes.cjs +636 -0
- package/dist/cjs/generateTypes.d.cts +104 -0
- package/dist/cjs/loader.cjs +65 -55
- package/dist/cjs/loader.d.cts +1 -0
- package/dist/cjs/loaderInternals.cjs +108 -0
- package/dist/cjs/loaderInternals.d.cts +23 -0
- package/dist/cjs/moduleGraph.cjs +431 -0
- package/dist/cjs/moduleGraph.d.cts +15 -0
- package/dist/cjs/moduleInfo.cjs +62 -0
- package/dist/cjs/moduleInfo.d.cts +10 -0
- package/dist/cjs/sassInternals.cjs +135 -0
- package/dist/cjs/sassInternals.d.cts +25 -0
- package/dist/cjs/stableNamespace.cjs +12 -0
- package/dist/cjs/stableNamespace.d.cts +3 -0
- package/dist/cjs/stableSelectors.cjs +44 -0
- package/dist/cjs/stableSelectors.d.cts +13 -0
- package/dist/cjs/stableSelectorsLiteral.cjs +104 -0
- package/dist/cjs/stableSelectorsLiteral.d.cts +19 -0
- package/dist/cjs/types.cjs +2 -0
- package/dist/cjs/types.d.cts +4 -0
- package/dist/css.d.ts +5 -6
- package/dist/css.js +101 -23
- package/dist/generateTypes.d.ts +104 -0
- package/dist/generateTypes.js +628 -0
- package/dist/loader.d.ts +1 -0
- package/dist/loader.js +63 -53
- package/dist/loaderInternals.d.ts +23 -0
- package/dist/loaderInternals.js +96 -0
- package/dist/moduleGraph.d.ts +15 -0
- package/dist/moduleGraph.js +425 -0
- package/dist/moduleInfo.d.ts +10 -0
- package/dist/moduleInfo.js +55 -0
- package/dist/sassInternals.d.ts +25 -0
- package/dist/sassInternals.js +124 -0
- package/dist/stableNamespace.d.ts +3 -0
- package/dist/stableNamespace.js +8 -0
- package/dist/stableSelectors.d.ts +13 -0
- package/dist/stableSelectors.js +36 -0
- package/dist/stableSelectorsLiteral.d.ts +19 -0
- package/dist/stableSelectorsLiteral.js +98 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/loader-queries.d.ts +61 -0
- package/package.json +58 -8
- package/stable/_index.scss +57 -0
- package/stable/stable.css +15 -0
- package/types-stub/index.d.ts +5 -0
- package/types.d.ts +4 -0
|
@@ -0,0 +1,636 @@
|
|
|
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.__generateTypesInternals = void 0;
|
|
7
|
+
exports.generateTypes = generateTypes;
|
|
8
|
+
exports.runGenerateTypesCli = runGenerateTypesCli;
|
|
9
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const node_module_1 = require("node:module");
|
|
13
|
+
const node_url_1 = require("node:url");
|
|
14
|
+
const es_module_lexer_1 = require("es-module-lexer");
|
|
15
|
+
const node_module_type_1 = require("node-module-type");
|
|
16
|
+
const get_tsconfig_1 = require("get-tsconfig");
|
|
17
|
+
const tsconfig_paths_1 = require("tsconfig-paths");
|
|
18
|
+
const css_js_1 = require("./css.cjs");
|
|
19
|
+
const loaderInternals_js_1 = require("./loaderInternals.cjs");
|
|
20
|
+
const stableSelectorsLiteral_js_1 = require("./stableSelectorsLiteral.cjs");
|
|
21
|
+
const stableNamespace_js_1 = require("./stableNamespace.cjs");
|
|
22
|
+
let activeCssWithMeta = css_js_1.cssWithMeta;
|
|
23
|
+
const DEFAULT_SKIP_DIRS = new Set([
|
|
24
|
+
'node_modules',
|
|
25
|
+
'.git',
|
|
26
|
+
'dist',
|
|
27
|
+
'build',
|
|
28
|
+
'coverage',
|
|
29
|
+
'.knighted-css',
|
|
30
|
+
'.next',
|
|
31
|
+
'.nuxt',
|
|
32
|
+
'.svelte-kit',
|
|
33
|
+
'.output',
|
|
34
|
+
'tmp',
|
|
35
|
+
]);
|
|
36
|
+
const SUPPORTED_EXTENSIONS = new Set([
|
|
37
|
+
'.ts',
|
|
38
|
+
'.tsx',
|
|
39
|
+
'.js',
|
|
40
|
+
'.jsx',
|
|
41
|
+
'.mts',
|
|
42
|
+
'.cts',
|
|
43
|
+
'.mjs',
|
|
44
|
+
'.cjs',
|
|
45
|
+
]);
|
|
46
|
+
let moduleTypeDetector = node_module_type_1.moduleType;
|
|
47
|
+
let importMetaUrlProvider = getImportMetaUrl;
|
|
48
|
+
function resolvePackageRoot() {
|
|
49
|
+
const detectedType = moduleTypeDetector();
|
|
50
|
+
if (detectedType === 'commonjs' && typeof __dirname === 'string') {
|
|
51
|
+
return node_path_1.default.resolve(__dirname, '..');
|
|
52
|
+
}
|
|
53
|
+
const moduleUrl = importMetaUrlProvider();
|
|
54
|
+
if (moduleUrl) {
|
|
55
|
+
return node_path_1.default.resolve(node_path_1.default.dirname((0, node_url_1.fileURLToPath)(moduleUrl)), '..');
|
|
56
|
+
}
|
|
57
|
+
return node_path_1.default.resolve(process.cwd(), 'node_modules', '@knighted', 'css');
|
|
58
|
+
}
|
|
59
|
+
function getImportMetaUrl() {
|
|
60
|
+
try {
|
|
61
|
+
return (0, eval)('import.meta.url');
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const PACKAGE_ROOT = resolvePackageRoot();
|
|
68
|
+
const DEFAULT_TYPES_ROOT = node_path_1.default.join(PACKAGE_ROOT, 'types-stub');
|
|
69
|
+
const DEFAULT_OUT_DIR = node_path_1.default.join(PACKAGE_ROOT, 'node_modules', '.knighted-css');
|
|
70
|
+
async function generateTypes(options = {}) {
|
|
71
|
+
const rootDir = node_path_1.default.resolve(options.rootDir ?? process.cwd());
|
|
72
|
+
const include = normalizeIncludeOptions(options.include, rootDir);
|
|
73
|
+
const outDir = node_path_1.default.resolve(options.outDir ?? DEFAULT_OUT_DIR);
|
|
74
|
+
const typesRoot = node_path_1.default.resolve(options.typesRoot ?? DEFAULT_TYPES_ROOT);
|
|
75
|
+
const tsconfig = loadTsconfigResolutionContext(rootDir);
|
|
76
|
+
await es_module_lexer_1.init;
|
|
77
|
+
await promises_1.default.mkdir(outDir, { recursive: true });
|
|
78
|
+
await promises_1.default.mkdir(typesRoot, { recursive: true });
|
|
79
|
+
const internalOptions = {
|
|
80
|
+
rootDir,
|
|
81
|
+
include,
|
|
82
|
+
outDir,
|
|
83
|
+
typesRoot,
|
|
84
|
+
stableNamespace: options.stableNamespace,
|
|
85
|
+
tsconfig,
|
|
86
|
+
};
|
|
87
|
+
return generateDeclarations(internalOptions);
|
|
88
|
+
}
|
|
89
|
+
async function generateDeclarations(options) {
|
|
90
|
+
const peerResolver = createProjectPeerResolver(options.rootDir);
|
|
91
|
+
const files = await collectCandidateFiles(options.include);
|
|
92
|
+
const manifestPath = node_path_1.default.join(options.outDir, 'manifest.json');
|
|
93
|
+
const previousManifest = await readManifest(manifestPath);
|
|
94
|
+
const nextManifest = {};
|
|
95
|
+
const selectorCache = new Map();
|
|
96
|
+
const processedSpecifiers = new Set();
|
|
97
|
+
const declarations = [];
|
|
98
|
+
const warnings = [];
|
|
99
|
+
let writes = 0;
|
|
100
|
+
for (const filePath of files) {
|
|
101
|
+
const matches = await findSpecifierImports(filePath);
|
|
102
|
+
for (const match of matches) {
|
|
103
|
+
const cleaned = match.specifier.trim();
|
|
104
|
+
const inlineFree = stripInlineLoader(cleaned);
|
|
105
|
+
if (!inlineFree.includes('?knighted-css'))
|
|
106
|
+
continue;
|
|
107
|
+
const { resource, query } = splitResourceAndQuery(inlineFree);
|
|
108
|
+
if (!query || !(0, loaderInternals_js_1.hasQueryFlag)(query, loaderInternals_js_1.TYPES_QUERY_FLAG)) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (processedSpecifiers.has(cleaned)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const resolvedNamespace = (0, stableNamespace_js_1.resolveStableNamespace)(options.stableNamespace);
|
|
115
|
+
const resolvedPath = await resolveImportPath(resource, match.importer, options.rootDir, options.tsconfig);
|
|
116
|
+
if (!resolvedPath) {
|
|
117
|
+
warnings.push(`Unable to resolve ${resource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
|
|
121
|
+
let selectorMap = selectorCache.get(cacheKey);
|
|
122
|
+
if (!selectorMap) {
|
|
123
|
+
try {
|
|
124
|
+
const { css } = await activeCssWithMeta(resolvedPath, {
|
|
125
|
+
cwd: options.rootDir,
|
|
126
|
+
peerResolver,
|
|
127
|
+
});
|
|
128
|
+
selectorMap = (0, stableSelectorsLiteral_js_1.buildStableSelectorsLiteral)({
|
|
129
|
+
css,
|
|
130
|
+
namespace: resolvedNamespace,
|
|
131
|
+
resourcePath: resolvedPath,
|
|
132
|
+
emitWarning: message => warnings.push(message),
|
|
133
|
+
}).selectorMap;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
warnings.push(`Failed to extract CSS for ${relativeToRoot(resolvedPath, options.rootDir)}: ${formatErrorMessage(error)}`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
selectorCache.set(cacheKey, selectorMap);
|
|
140
|
+
}
|
|
141
|
+
const variant = (0, loaderInternals_js_1.determineSelectorVariant)(query);
|
|
142
|
+
const declaration = formatModuleDeclaration(cleaned, variant, selectorMap);
|
|
143
|
+
const declarationHash = hashContent(declaration);
|
|
144
|
+
const fileName = buildDeclarationFileName(cleaned);
|
|
145
|
+
const targetPath = node_path_1.default.join(options.outDir, fileName);
|
|
146
|
+
const previousEntry = previousManifest[cleaned];
|
|
147
|
+
const needsWrite = previousEntry?.hash !== declarationHash || !(await fileExists(targetPath));
|
|
148
|
+
if (needsWrite) {
|
|
149
|
+
await promises_1.default.writeFile(targetPath, declaration, 'utf8');
|
|
150
|
+
writes += 1;
|
|
151
|
+
}
|
|
152
|
+
nextManifest[cleaned] = { file: fileName, hash: declarationHash };
|
|
153
|
+
if (needsWrite) {
|
|
154
|
+
declarations.push({ specifier: cleaned, filePath: targetPath });
|
|
155
|
+
}
|
|
156
|
+
processedSpecifiers.add(cleaned);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const removed = await removeStaleDeclarations(previousManifest, nextManifest, options.outDir);
|
|
160
|
+
await writeManifest(manifestPath, nextManifest);
|
|
161
|
+
const typesIndexPath = node_path_1.default.join(options.typesRoot, 'index.d.ts');
|
|
162
|
+
await writeTypesIndex(typesIndexPath, nextManifest, options.outDir);
|
|
163
|
+
if (Object.keys(nextManifest).length === 0) {
|
|
164
|
+
declarations.length = 0;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
written: writes,
|
|
168
|
+
removed,
|
|
169
|
+
declarations,
|
|
170
|
+
warnings,
|
|
171
|
+
outDir: options.outDir,
|
|
172
|
+
typesIndexPath,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function normalizeIncludeOptions(include, rootDir) {
|
|
176
|
+
if (!include || include.length === 0) {
|
|
177
|
+
return [rootDir];
|
|
178
|
+
}
|
|
179
|
+
return include.map(entry => node_path_1.default.isAbsolute(entry) ? entry : node_path_1.default.resolve(rootDir, entry));
|
|
180
|
+
}
|
|
181
|
+
async function collectCandidateFiles(entries) {
|
|
182
|
+
const files = [];
|
|
183
|
+
const visited = new Set();
|
|
184
|
+
async function walk(entryPath) {
|
|
185
|
+
const resolved = node_path_1.default.resolve(entryPath);
|
|
186
|
+
if (visited.has(resolved)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
visited.add(resolved);
|
|
190
|
+
let stat;
|
|
191
|
+
try {
|
|
192
|
+
stat = await promises_1.default.stat(resolved);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (stat.isDirectory()) {
|
|
198
|
+
const base = node_path_1.default.basename(resolved);
|
|
199
|
+
if (DEFAULT_SKIP_DIRS.has(base)) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const children = await promises_1.default.readdir(resolved, { withFileTypes: true });
|
|
203
|
+
for (const child of children) {
|
|
204
|
+
await walk(node_path_1.default.join(resolved, child.name));
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (!stat.isFile()) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const ext = node_path_1.default.extname(resolved).toLowerCase();
|
|
212
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
files.push(resolved);
|
|
216
|
+
}
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
await walk(entry);
|
|
219
|
+
}
|
|
220
|
+
return files;
|
|
221
|
+
}
|
|
222
|
+
async function findSpecifierImports(filePath) {
|
|
223
|
+
let source;
|
|
224
|
+
try {
|
|
225
|
+
source = await promises_1.default.readFile(filePath, 'utf8');
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
if (!source.includes('?knighted-css')) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
const matches = [];
|
|
234
|
+
const [imports] = (0, es_module_lexer_1.parse)(source, filePath);
|
|
235
|
+
for (const record of imports) {
|
|
236
|
+
const specifier = record.n ?? source.slice(record.s, record.e);
|
|
237
|
+
if (specifier && specifier.includes('?knighted-css')) {
|
|
238
|
+
matches.push({ specifier, importer: filePath });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const requireRegex = /require\((['"])([^'"`]+?\?knighted-css[^'"`]*)\1\)/g;
|
|
242
|
+
let reqMatch;
|
|
243
|
+
while ((reqMatch = requireRegex.exec(source)) !== null) {
|
|
244
|
+
const spec = reqMatch[2];
|
|
245
|
+
if (spec) {
|
|
246
|
+
matches.push({ specifier: spec, importer: filePath });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return matches;
|
|
250
|
+
}
|
|
251
|
+
function stripInlineLoader(specifier) {
|
|
252
|
+
const idx = specifier.lastIndexOf('!');
|
|
253
|
+
return idx >= 0 ? specifier.slice(idx + 1) : specifier;
|
|
254
|
+
}
|
|
255
|
+
function splitResourceAndQuery(specifier) {
|
|
256
|
+
const hashIndex = specifier.indexOf('#');
|
|
257
|
+
const trimmed = hashIndex >= 0 ? specifier.slice(0, hashIndex) : specifier;
|
|
258
|
+
const queryIndex = trimmed.indexOf('?');
|
|
259
|
+
if (queryIndex < 0) {
|
|
260
|
+
return { resource: trimmed, query: '' };
|
|
261
|
+
}
|
|
262
|
+
return { resource: trimmed.slice(0, queryIndex), query: trimmed.slice(queryIndex) };
|
|
263
|
+
}
|
|
264
|
+
const projectRequireCache = new Map();
|
|
265
|
+
async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tsconfig) {
|
|
266
|
+
if (!resourceSpecifier)
|
|
267
|
+
return undefined;
|
|
268
|
+
if (resourceSpecifier.startsWith('.')) {
|
|
269
|
+
return node_path_1.default.resolve(node_path_1.default.dirname(importerPath), resourceSpecifier);
|
|
270
|
+
}
|
|
271
|
+
if (resourceSpecifier.startsWith('/')) {
|
|
272
|
+
return node_path_1.default.resolve(rootDir, resourceSpecifier.slice(1));
|
|
273
|
+
}
|
|
274
|
+
const tsconfigResolved = await resolveWithTsconfigPaths(resourceSpecifier, tsconfig);
|
|
275
|
+
if (tsconfigResolved) {
|
|
276
|
+
return tsconfigResolved;
|
|
277
|
+
}
|
|
278
|
+
const requireFromRoot = getProjectRequire(rootDir);
|
|
279
|
+
try {
|
|
280
|
+
return requireFromRoot.resolve(resourceSpecifier);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function buildDeclarationFileName(specifier) {
|
|
287
|
+
const digest = node_crypto_1.default.createHash('sha1').update(specifier).digest('hex').slice(0, 12);
|
|
288
|
+
return `knt-${digest}.d.ts`;
|
|
289
|
+
}
|
|
290
|
+
function formatModuleDeclaration(specifier, variant, selectors) {
|
|
291
|
+
const literalSpecifier = JSON.stringify(specifier);
|
|
292
|
+
const selectorType = formatSelectorType(selectors);
|
|
293
|
+
const header = `declare module ${literalSpecifier} {`;
|
|
294
|
+
const footer = '}';
|
|
295
|
+
if (variant === 'types') {
|
|
296
|
+
return `${header}
|
|
297
|
+
export const knightedCss: string
|
|
298
|
+
export const stableSelectors: ${selectorType}
|
|
299
|
+
${footer}
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
const stableLine = ` export const stableSelectors: ${selectorType}`;
|
|
303
|
+
const shared = ` const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
304
|
+
export const knightedCss: string
|
|
305
|
+
${stableLine}`;
|
|
306
|
+
if (variant === 'combined') {
|
|
307
|
+
return `${header}
|
|
308
|
+
${shared}
|
|
309
|
+
export default combined
|
|
310
|
+
${footer}
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
return `${header}
|
|
314
|
+
${shared}
|
|
315
|
+
${footer}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
function formatSelectorType(selectors) {
|
|
319
|
+
if (selectors.size === 0) {
|
|
320
|
+
return 'Readonly<Record<string, string>>';
|
|
321
|
+
}
|
|
322
|
+
const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
323
|
+
const lines = entries.map(([token, selector]) => ` readonly ${JSON.stringify(token)}: ${JSON.stringify(selector)}`);
|
|
324
|
+
return `Readonly<{
|
|
325
|
+
${lines.join('\n')}
|
|
326
|
+
}>`;
|
|
327
|
+
}
|
|
328
|
+
function hashContent(content) {
|
|
329
|
+
return node_crypto_1.default.createHash('sha1').update(content).digest('hex');
|
|
330
|
+
}
|
|
331
|
+
async function readManifest(manifestPath) {
|
|
332
|
+
try {
|
|
333
|
+
const raw = await promises_1.default.readFile(manifestPath, 'utf8');
|
|
334
|
+
return JSON.parse(raw);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
return {};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async function writeManifest(manifestPath, manifest) {
|
|
341
|
+
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
342
|
+
}
|
|
343
|
+
async function removeStaleDeclarations(previous, next, outDir) {
|
|
344
|
+
const stale = Object.entries(previous).filter(([specifier]) => !next[specifier]);
|
|
345
|
+
let removed = 0;
|
|
346
|
+
for (const [, entry] of stale) {
|
|
347
|
+
const targetPath = node_path_1.default.join(outDir, entry.file);
|
|
348
|
+
try {
|
|
349
|
+
await promises_1.default.unlink(targetPath);
|
|
350
|
+
removed += 1;
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// ignore
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return removed;
|
|
357
|
+
}
|
|
358
|
+
async function writeTypesIndex(indexPath, manifest, outDir) {
|
|
359
|
+
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
360
|
+
const references = Object.values(manifest)
|
|
361
|
+
.sort((a, b) => a.file.localeCompare(b.file))
|
|
362
|
+
.map(entry => {
|
|
363
|
+
const rel = node_path_1.default
|
|
364
|
+
.relative(node_path_1.default.dirname(indexPath), node_path_1.default.join(outDir, entry.file))
|
|
365
|
+
.split(node_path_1.default.sep)
|
|
366
|
+
.join('/');
|
|
367
|
+
return `/// <reference path="${rel}" />`;
|
|
368
|
+
});
|
|
369
|
+
const content = references.length > 0
|
|
370
|
+
? `${header}
|
|
371
|
+
${references.join('\n')}
|
|
372
|
+
`
|
|
373
|
+
: `${header}
|
|
374
|
+
`;
|
|
375
|
+
await promises_1.default.writeFile(indexPath, content, 'utf8');
|
|
376
|
+
}
|
|
377
|
+
function formatErrorMessage(error) {
|
|
378
|
+
if (error instanceof Error && typeof error.message === 'string') {
|
|
379
|
+
return error.message;
|
|
380
|
+
}
|
|
381
|
+
return String(error);
|
|
382
|
+
}
|
|
383
|
+
function relativeToRoot(filePath, rootDir) {
|
|
384
|
+
return node_path_1.default.relative(rootDir, filePath) || filePath;
|
|
385
|
+
}
|
|
386
|
+
async function fileExists(target) {
|
|
387
|
+
try {
|
|
388
|
+
await promises_1.default.access(target);
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async function resolveWithTsconfigPaths(specifier, tsconfig) {
|
|
396
|
+
if (!tsconfig) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
if (tsconfig.matchPath) {
|
|
400
|
+
const matched = tsconfig.matchPath(specifier);
|
|
401
|
+
if (matched && (await fileExists(matched))) {
|
|
402
|
+
return matched;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (tsconfig.absoluteBaseUrl && isNonRelativeSpecifier(specifier)) {
|
|
406
|
+
const candidate = node_path_1.default.join(tsconfig.absoluteBaseUrl, specifier.split('/').join(node_path_1.default.sep));
|
|
407
|
+
if (await fileExists(candidate)) {
|
|
408
|
+
return candidate;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
function loadTsconfigResolutionContext(rootDir, loader = get_tsconfig_1.getTsconfig) {
|
|
414
|
+
let result;
|
|
415
|
+
try {
|
|
416
|
+
result = loader(rootDir);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
if (!result) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
const compilerOptions = result.config.compilerOptions ?? {};
|
|
425
|
+
const configDir = node_path_1.default.dirname(result.path);
|
|
426
|
+
const absoluteBaseUrl = compilerOptions.baseUrl
|
|
427
|
+
? node_path_1.default.resolve(configDir, compilerOptions.baseUrl)
|
|
428
|
+
: undefined;
|
|
429
|
+
const normalizedPaths = normalizeTsconfigPaths(compilerOptions.paths);
|
|
430
|
+
const matchPath = absoluteBaseUrl && normalizedPaths
|
|
431
|
+
? (0, tsconfig_paths_1.createMatchPath)(absoluteBaseUrl, normalizedPaths)
|
|
432
|
+
: undefined;
|
|
433
|
+
if (!absoluteBaseUrl && !matchPath) {
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
return { absoluteBaseUrl, matchPath };
|
|
437
|
+
}
|
|
438
|
+
function normalizeTsconfigPaths(paths) {
|
|
439
|
+
if (!paths) {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
const normalized = {};
|
|
443
|
+
for (const [pattern, replacements] of Object.entries(paths)) {
|
|
444
|
+
if (!replacements) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const values = Array.isArray(replacements) ? replacements : [replacements];
|
|
448
|
+
if (values.length === 0) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
normalized[pattern] = values;
|
|
452
|
+
}
|
|
453
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
454
|
+
}
|
|
455
|
+
function isNonRelativeSpecifier(specifier) {
|
|
456
|
+
if (!specifier) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
function createProjectPeerResolver(rootDir) {
|
|
468
|
+
const resolver = getProjectRequire(rootDir);
|
|
469
|
+
return async (name) => {
|
|
470
|
+
const resolved = resolver.resolve(name);
|
|
471
|
+
return import((0, node_url_1.pathToFileURL)(resolved).href);
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function getProjectRequire(rootDir) {
|
|
475
|
+
const cached = projectRequireCache.get(rootDir);
|
|
476
|
+
if (cached) {
|
|
477
|
+
return cached;
|
|
478
|
+
}
|
|
479
|
+
const anchor = node_path_1.default.join(rootDir, 'package.json');
|
|
480
|
+
let loader;
|
|
481
|
+
try {
|
|
482
|
+
loader = (0, node_module_1.createRequire)(anchor);
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
loader = (0, node_module_1.createRequire)(node_path_1.default.join(process.cwd(), 'package.json'));
|
|
486
|
+
}
|
|
487
|
+
projectRequireCache.set(rootDir, loader);
|
|
488
|
+
return loader;
|
|
489
|
+
}
|
|
490
|
+
async function runGenerateTypesCli(argv = process.argv.slice(2)) {
|
|
491
|
+
let parsed;
|
|
492
|
+
try {
|
|
493
|
+
parsed = parseCliArgs(argv);
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
console.error(`[knighted-css] ${formatErrorMessage(error)}`);
|
|
497
|
+
process.exitCode = 1;
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (parsed.help) {
|
|
501
|
+
printHelp();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const result = await generateTypes({
|
|
506
|
+
rootDir: parsed.rootDir,
|
|
507
|
+
include: parsed.include,
|
|
508
|
+
outDir: parsed.outDir,
|
|
509
|
+
typesRoot: parsed.typesRoot,
|
|
510
|
+
stableNamespace: parsed.stableNamespace,
|
|
511
|
+
});
|
|
512
|
+
reportCliResult(result);
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
console.error('[knighted-css] generate-types failed.');
|
|
516
|
+
console.error(error);
|
|
517
|
+
process.exitCode = 1;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function parseCliArgs(argv) {
|
|
521
|
+
let rootDir = process.cwd();
|
|
522
|
+
const include = [];
|
|
523
|
+
let outDir;
|
|
524
|
+
let typesRoot;
|
|
525
|
+
let stableNamespace;
|
|
526
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
527
|
+
const arg = argv[i];
|
|
528
|
+
if (arg === '--help' || arg === '-h') {
|
|
529
|
+
return { rootDir, include, outDir, typesRoot, stableNamespace, help: true };
|
|
530
|
+
}
|
|
531
|
+
if (arg === '--root' || arg === '-r') {
|
|
532
|
+
const value = argv[++i];
|
|
533
|
+
if (!value) {
|
|
534
|
+
throw new Error('Missing value for --root');
|
|
535
|
+
}
|
|
536
|
+
rootDir = node_path_1.default.resolve(value);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
if (arg === '--include' || arg === '-i') {
|
|
540
|
+
const value = argv[++i];
|
|
541
|
+
if (!value) {
|
|
542
|
+
throw new Error('Missing value for --include');
|
|
543
|
+
}
|
|
544
|
+
include.push(value);
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (arg === '--out-dir') {
|
|
548
|
+
const value = argv[++i];
|
|
549
|
+
if (!value) {
|
|
550
|
+
throw new Error('Missing value for --out-dir');
|
|
551
|
+
}
|
|
552
|
+
outDir = value;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (arg === '--types-root') {
|
|
556
|
+
const value = argv[++i];
|
|
557
|
+
if (!value) {
|
|
558
|
+
throw new Error('Missing value for --types-root');
|
|
559
|
+
}
|
|
560
|
+
typesRoot = value;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (arg === '--stable-namespace') {
|
|
564
|
+
const value = argv[++i];
|
|
565
|
+
if (!value) {
|
|
566
|
+
throw new Error('Missing value for --stable-namespace');
|
|
567
|
+
}
|
|
568
|
+
stableNamespace = value;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (arg.startsWith('-')) {
|
|
572
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
573
|
+
}
|
|
574
|
+
include.push(arg);
|
|
575
|
+
}
|
|
576
|
+
return { rootDir, include, outDir, typesRoot, stableNamespace };
|
|
577
|
+
}
|
|
578
|
+
function printHelp() {
|
|
579
|
+
console.log(`Usage: knighted-css-generate-types [options]
|
|
580
|
+
|
|
581
|
+
Options:
|
|
582
|
+
-r, --root <path> Project root directory (default: cwd)
|
|
583
|
+
-i, --include <path> Additional directories/files to scan (repeatable)
|
|
584
|
+
--out-dir <path> Output directory for generated declarations
|
|
585
|
+
--types-root <path> Directory for generated @types entrypoint
|
|
586
|
+
--stable-namespace <name> Stable namespace prefix for generated selector maps
|
|
587
|
+
-h, --help Show this help message
|
|
588
|
+
`);
|
|
589
|
+
}
|
|
590
|
+
function reportCliResult(result) {
|
|
591
|
+
if (result.written === 0 && result.removed === 0) {
|
|
592
|
+
console.log('[knighted-css] No changes to ?knighted-css&types declarations (cache is up to date).');
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
console.log(`[knighted-css] Updated ${result.written} declaration(s), removed ${result.removed}, output in ${result.outDir}.`);
|
|
596
|
+
}
|
|
597
|
+
console.log(`[knighted-css] Type references: ${result.typesIndexPath}`);
|
|
598
|
+
for (const warning of result.warnings) {
|
|
599
|
+
console.warn(`[knighted-css] ${warning}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function setCssWithMetaImplementation(impl) {
|
|
603
|
+
activeCssWithMeta = impl ?? css_js_1.cssWithMeta;
|
|
604
|
+
}
|
|
605
|
+
function setModuleTypeDetector(detector) {
|
|
606
|
+
moduleTypeDetector = detector ?? node_module_type_1.moduleType;
|
|
607
|
+
}
|
|
608
|
+
function setImportMetaUrlProvider(provider) {
|
|
609
|
+
importMetaUrlProvider = provider ?? getImportMetaUrl;
|
|
610
|
+
}
|
|
611
|
+
exports.__generateTypesInternals = {
|
|
612
|
+
writeTypesIndex,
|
|
613
|
+
stripInlineLoader,
|
|
614
|
+
splitResourceAndQuery,
|
|
615
|
+
findSpecifierImports,
|
|
616
|
+
resolveImportPath,
|
|
617
|
+
resolvePackageRoot,
|
|
618
|
+
buildDeclarationFileName,
|
|
619
|
+
formatModuleDeclaration,
|
|
620
|
+
formatSelectorType,
|
|
621
|
+
relativeToRoot,
|
|
622
|
+
collectCandidateFiles,
|
|
623
|
+
normalizeIncludeOptions,
|
|
624
|
+
normalizeTsconfigPaths,
|
|
625
|
+
setCssWithMetaImplementation,
|
|
626
|
+
setModuleTypeDetector,
|
|
627
|
+
setImportMetaUrlProvider,
|
|
628
|
+
isNonRelativeSpecifier,
|
|
629
|
+
createProjectPeerResolver,
|
|
630
|
+
getProjectRequire,
|
|
631
|
+
loadTsconfigResolutionContext,
|
|
632
|
+
resolveWithTsconfigPaths,
|
|
633
|
+
parseCliArgs,
|
|
634
|
+
printHelp,
|
|
635
|
+
reportCliResult,
|
|
636
|
+
};
|