svelte-ag 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.js +386 -0
- package/dist/bin/build-tailwind-manifest/cli.d.ts +2 -0
- package/dist/bin/build-tailwind-manifest/cli.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/cli.js +122 -0
- package/dist/bin/build-tailwind-manifest/graph.d.ts +9 -0
- package/dist/bin/build-tailwind-manifest/graph.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/graph.js +405 -0
- package/dist/bin/build-tailwind-manifest/index.d.ts +1 -0
- package/dist/bin/build-tailwind-manifest/index.js +6 -0
- package/dist/bin/build-tailwind-manifest/manifest-generator.d.ts +7 -0
- package/dist/bin/build-tailwind-manifest/manifest-generator.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/manifest-generator.js +85 -0
- package/dist/bin/build-tailwind-manifest/path-utils.d.ts +6 -0
- package/dist/bin/build-tailwind-manifest/path-utils.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/path-utils.js +41 -0
- package/dist/bin/build-tailwind-manifest/types.d.ts +15 -0
- package/dist/bin/build-tailwind-manifest/types.d.ts.map +1 -0
- package/dist/bin/build-tailwind-manifest/types.js +1 -0
- package/dist/tailwind-sources.manifest.jsonc +193 -3
- package/dist/vite/vite-plugin-component-source-collector.d.ts.map +1 -1
- package/dist/vite/vite-plugin-component-source-collector.js +1 -6
- package/package.json +4 -3
- package/src/lib/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.ts +591 -0
- package/src/lib/bin/build-tailwind-manifest/cli.ts +147 -0
- package/src/lib/bin/build-tailwind-manifest/graph.ts +521 -0
- package/src/lib/bin/build-tailwind-manifest/index.ts +8 -0
- package/src/lib/bin/build-tailwind-manifest/manifest-generator.ts +119 -0
- package/src/lib/bin/build-tailwind-manifest/path-utils.ts +53 -0
- package/src/lib/bin/build-tailwind-manifest/types.ts +18 -0
- package/src/lib/vite/vite-plugin-component-source-collector.ts +1 -6
- package/dist/bin/build-tailwind-manifest.d.ts +0 -15
- package/dist/bin/build-tailwind-manifest.d.ts.map +0 -1
- package/dist/bin/build-tailwind-manifest.js +0 -581
- package/dist/bin/build-tailwind-manifest.unit.test.d.ts.map +0 -1
- package/dist/bin/build-tailwind-manifest.unit.test.js +0 -208
- package/src/lib/bin/build-tailwind-manifest.ts +0 -757
- package/src/lib/bin/build-tailwind-manifest.unit.test.ts +0 -309
- /package/dist/bin/{build-tailwind-manifest.unit.test.d.ts → build-tailwind-manifest/build-tailwind-manifest.unit.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-tailwind-manifest.unit.test.d.ts","sourceRoot":"","sources":["../../../src/lib/bin/build-tailwind-manifest/build-tailwind-manifest.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { parseTailwindSourceManifest } from '../../vite/tailwind-sources-manifest.js';
|
|
6
|
+
import { main } from './cli.js';
|
|
7
|
+
import { createGraphScanner } from './graph.js';
|
|
8
|
+
import { generateTailwindManifestForPackage } from './manifest-generator.js';
|
|
9
|
+
const tempDirectories = [];
|
|
10
|
+
const originalWorkingDirectory = process.cwd();
|
|
11
|
+
async function createTempDirectory(prefix) {
|
|
12
|
+
const directory = await mkdtemp(join(tmpdir(), prefix));
|
|
13
|
+
tempDirectories.push(directory);
|
|
14
|
+
return directory;
|
|
15
|
+
}
|
|
16
|
+
async function writeJson(filePath, value) {
|
|
17
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
18
|
+
}
|
|
19
|
+
async function withTimeout(promise, timeoutMs = 5000) {
|
|
20
|
+
let timer;
|
|
21
|
+
try {
|
|
22
|
+
return await Promise.race([
|
|
23
|
+
promise,
|
|
24
|
+
new Promise((_, reject) => {
|
|
25
|
+
timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
26
|
+
})
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
if (timer)
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
describe('build-tailwind-manifest', () => {
|
|
35
|
+
afterEach(async () => {
|
|
36
|
+
process.chdir(originalWorkingDirectory);
|
|
37
|
+
await Promise.all(tempDirectories.splice(0).map((directory) => rm(directory, {
|
|
38
|
+
recursive: true,
|
|
39
|
+
force: true
|
|
40
|
+
})));
|
|
41
|
+
});
|
|
42
|
+
it('generates manifests for matching package.json globs and filters export prefixes', async () => {
|
|
43
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-monorepo-');
|
|
44
|
+
const packageRoot = join(repoRoot, 'packages', 'module-a');
|
|
45
|
+
await mkdir(join(packageRoot, 'dist', 'admin', 'overrides'), { recursive: true });
|
|
46
|
+
await mkdir(join(packageRoot, 'dist', 'project'), { recursive: true });
|
|
47
|
+
await mkdir(join(packageRoot, 'testing', 'dist'), { recursive: true });
|
|
48
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
49
|
+
name: 'module-a',
|
|
50
|
+
private: true,
|
|
51
|
+
type: 'module',
|
|
52
|
+
exports: {
|
|
53
|
+
'./testing/*': './testing/dist/*',
|
|
54
|
+
'./admin': './dist/admin/index.ts',
|
|
55
|
+
'./admin/overrides': './dist/admin/overrides/index.ts',
|
|
56
|
+
'./project': './dist/project/index.ts'
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'index.ts'), "export { default as AdminPanel } from './AdminPanel.svelte';\n");
|
|
60
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'AdminPanel.svelte'), '<div class="admin-grid gap-4"></div>\n');
|
|
61
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'overrides', 'index.ts'), "export { default as AdminOverride } from './AdminOverride.svelte';\n");
|
|
62
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'overrides', 'AdminOverride.svelte'), [
|
|
63
|
+
'<script module lang="ts">',
|
|
64
|
+
" export { overrideVariants } from './variants.ts';",
|
|
65
|
+
'</script>',
|
|
66
|
+
'<script lang="ts">',
|
|
67
|
+
" import { cn } from './util.js';",
|
|
68
|
+
' let { class: className } = $props();',
|
|
69
|
+
'</script>',
|
|
70
|
+
"<div class={cn('override-flex px-2', className)}></div>",
|
|
71
|
+
''
|
|
72
|
+
].join('\n'));
|
|
73
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'overrides', 'variants.ts'), "export const overrideVariants = { class: 'override-variant rounded-md' };\n");
|
|
74
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'overrides', 'util.js'), "export const cn = (...values) => values.join(' ');\n");
|
|
75
|
+
await writeFile(join(packageRoot, 'dist', 'project', 'index.ts'), ["import './theme.css';", "export { default as ProjectPanel } from './ProjectPanel.svelte';", ''].join('\n'));
|
|
76
|
+
await writeFile(join(packageRoot, 'dist', 'project', 'ProjectPanel.svelte'), '<div class="project-root"></div>\n');
|
|
77
|
+
await writeFile(join(packageRoot, 'dist', 'project', 'theme.css'), '.project-theme { color: red; }\n');
|
|
78
|
+
await writeFile(join(packageRoot, 'testing', 'dist', 'ignored.ts'), 'export const ignored = true;\n');
|
|
79
|
+
process.chdir(repoRoot);
|
|
80
|
+
await main(['--packages', 'packages/*/package.json', '--exports', 'admin,project']);
|
|
81
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
82
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
83
|
+
expect(manifestContents).toContain('/* tailwind-manifest export "./project"; styles from: ./dist/project/theme.css */');
|
|
84
|
+
expect(manifestContents).toContain('/* tailwind-manifest symbol "AdminPanel"; styles from: inline-only */');
|
|
85
|
+
expect(Object.keys(manifest.exports)).toEqual(['./admin', './admin/overrides', './project']);
|
|
86
|
+
expect(manifest.exports['./admin']?.classes).toEqual(['admin-grid', 'gap-4']);
|
|
87
|
+
expect(manifest.exports['./admin']?.symbols?.AdminPanel?.classes).toEqual(['admin-grid', 'gap-4']);
|
|
88
|
+
expect(manifest.exports['./admin/overrides']?.classes).toEqual([
|
|
89
|
+
'override-flex',
|
|
90
|
+
'override-variant',
|
|
91
|
+
'px-2',
|
|
92
|
+
'rounded-md'
|
|
93
|
+
]);
|
|
94
|
+
expect(manifest.exports['./project']?.classes).toEqual(['project-root']);
|
|
95
|
+
expect(manifest.exports['./project']?.sources).toEqual(['./dist/project/theme.css']);
|
|
96
|
+
});
|
|
97
|
+
it('coerces legacy .json manifest overrides to .jsonc output', async () => {
|
|
98
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-json-override-');
|
|
99
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
100
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
101
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
102
|
+
name: 'package-a',
|
|
103
|
+
private: true,
|
|
104
|
+
type: 'module',
|
|
105
|
+
tailwindSources: './dist/custom-tailwind.manifest.json',
|
|
106
|
+
exports: {
|
|
107
|
+
'./panel': './dist/panel.svelte'
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
await writeFile(join(packageRoot, 'dist', 'panel.svelte'), '<div class="panel-root"></div>\n');
|
|
111
|
+
process.chdir(packageRoot);
|
|
112
|
+
await main([]);
|
|
113
|
+
const jsoncPath = join(packageRoot, 'dist', 'custom-tailwind.manifest.jsonc');
|
|
114
|
+
const manifestContents = await readFile(jsoncPath, 'utf8');
|
|
115
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
116
|
+
expect(manifest.exports['./panel']?.classes).toEqual(['panel-root']);
|
|
117
|
+
});
|
|
118
|
+
it('handles root package exports declared as a string or conditional object', async () => {
|
|
119
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-root-exports-');
|
|
120
|
+
const stringExportRoot = join(repoRoot, 'packages', 'string-export');
|
|
121
|
+
const conditionalExportRoot = join(repoRoot, 'packages', 'conditional-export');
|
|
122
|
+
await mkdir(join(stringExportRoot, 'dist'), { recursive: true });
|
|
123
|
+
await mkdir(join(conditionalExportRoot, 'dist'), { recursive: true });
|
|
124
|
+
await writeJson(join(stringExportRoot, 'package.json'), {
|
|
125
|
+
name: 'string-export',
|
|
126
|
+
private: true,
|
|
127
|
+
type: 'module',
|
|
128
|
+
exports: './dist/index.ts'
|
|
129
|
+
});
|
|
130
|
+
await writeFile(join(stringExportRoot, 'dist', 'index.ts'), "export const rootCard = { class: 'root-card' };\n");
|
|
131
|
+
await writeJson(join(conditionalExportRoot, 'package.json'), {
|
|
132
|
+
name: 'conditional-export',
|
|
133
|
+
private: true,
|
|
134
|
+
type: 'module',
|
|
135
|
+
exports: {
|
|
136
|
+
types: './dist/index.d.ts',
|
|
137
|
+
import: './dist/index.ts'
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
await writeFile(join(conditionalExportRoot, 'dist', 'index.ts'), "export const conditionalCard = { class: 'conditional-card' };\n");
|
|
141
|
+
process.chdir(repoRoot);
|
|
142
|
+
await main(['--packages', 'packages/*/package.json', '--exports', '.']);
|
|
143
|
+
const stringManifest = parseTailwindSourceManifest(await readFile(join(stringExportRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8'));
|
|
144
|
+
const conditionalManifest = parseTailwindSourceManifest(await readFile(join(conditionalExportRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8'));
|
|
145
|
+
expect(Object.keys(stringManifest.exports)).toEqual(['.']);
|
|
146
|
+
expect(stringManifest.exports['.']?.classes).toEqual(['root-card']);
|
|
147
|
+
expect(Object.keys(conditionalManifest.exports)).toEqual(['.']);
|
|
148
|
+
expect(conditionalManifest.exports['.']?.classes).toEqual(['conditional-card']);
|
|
149
|
+
});
|
|
150
|
+
it('resolves emitted .js export specifiers back to .ts and .svelte.ts source files', async () => {
|
|
151
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-emitted-js-');
|
|
152
|
+
const packageRoot = join(repoRoot, 'packages', 'modules', 'contact');
|
|
153
|
+
await mkdir(join(packageRoot, 'project', 'dist', 'contact', 'components'), { recursive: true });
|
|
154
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
155
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
156
|
+
name: 'contact-module',
|
|
157
|
+
private: true,
|
|
158
|
+
type: 'module',
|
|
159
|
+
exports: {
|
|
160
|
+
'./project': './project/dist/index.ts'
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'index.ts'), [
|
|
164
|
+
"export { contactClientApiRequest, contactClientForm } from './api.svelte.js';",
|
|
165
|
+
"export * from './contact/index.js';",
|
|
166
|
+
"export * from './direct.js';",
|
|
167
|
+
''
|
|
168
|
+
].join('\n'));
|
|
169
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'api.svelte.ts'), "export const contactClientForm = { class: 'api-form-inline' };\n");
|
|
170
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'direct.ts'), "export const contactChip = { class: 'contact-chip' };\n");
|
|
171
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'contact', 'index.ts'), [
|
|
172
|
+
"export { default as Contact } from './components/Contact.svelte';",
|
|
173
|
+
"export { default as ContactField } from './components/ContactField.svelte';",
|
|
174
|
+
''
|
|
175
|
+
].join('\n'));
|
|
176
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'contact', 'components', 'Contact.svelte'), '<div class="flex flex-col items-center gap-2"></div>\n');
|
|
177
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'contact', 'components', 'ContactField.svelte'), '<div class="self-start w-full z-5"></div>\n');
|
|
178
|
+
process.chdir(packageRoot);
|
|
179
|
+
await main([]);
|
|
180
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
181
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
182
|
+
expect(manifest.exports['./project']?.classes).toEqual([
|
|
183
|
+
'api-form-inline',
|
|
184
|
+
'contact-chip',
|
|
185
|
+
'flex',
|
|
186
|
+
'flex-col',
|
|
187
|
+
'gap-2',
|
|
188
|
+
'items-center',
|
|
189
|
+
'self-start',
|
|
190
|
+
'w-full',
|
|
191
|
+
'z-5'
|
|
192
|
+
]);
|
|
193
|
+
expect(manifest.exports['./project']?.symbols?.contactChip?.classes).toEqual(['contact-chip']);
|
|
194
|
+
expect(manifest.exports['./project']?.symbols?.contactClientForm?.classes).toEqual(['api-form-inline']);
|
|
195
|
+
expect(manifest.exports['./project']?.symbols?.Contact?.classes).toEqual([
|
|
196
|
+
'flex',
|
|
197
|
+
'flex-col',
|
|
198
|
+
'gap-2',
|
|
199
|
+
'items-center'
|
|
200
|
+
]);
|
|
201
|
+
expect(manifest.exports['./project']?.symbols?.ContactField?.classes).toEqual(['self-start', 'w-full', 'z-5']);
|
|
202
|
+
});
|
|
203
|
+
it('completes concurrent overlapping graph scans on a shared scanner', async () => {
|
|
204
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-concurrent-scanner-');
|
|
205
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
206
|
+
await mkdir(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides'), { recursive: true });
|
|
207
|
+
await mkdir(join(packageRoot, 'project', 'dist'), { recursive: true });
|
|
208
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
209
|
+
name: 'package-a',
|
|
210
|
+
private: true,
|
|
211
|
+
type: 'module',
|
|
212
|
+
exports: {
|
|
213
|
+
'./admin': './admin/dist/lib/index.ts',
|
|
214
|
+
'./admin/overrides': './admin/dist/lib/components/preview/overrides/index.ts',
|
|
215
|
+
'./project': './project/dist/index.ts'
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'index.ts'), [
|
|
219
|
+
"export { sharedBadge } from './shared.ts';",
|
|
220
|
+
"export * from './components/preview/index.ts';",
|
|
221
|
+
"export { default as AdminPanel } from './components/AdminPanel.svelte';",
|
|
222
|
+
''
|
|
223
|
+
].join('\n'));
|
|
224
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'shared.ts'), "export const sharedBadge = { class: 'shared-badge rounded-md' };\n");
|
|
225
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'AdminPanel.svelte'), '<div class="admin-grid gap-4"></div>\n');
|
|
226
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'index.ts'), [
|
|
227
|
+
"export { default as PreviewPanel } from './PreviewPanel.svelte';",
|
|
228
|
+
"export * from './overrides/index.ts';",
|
|
229
|
+
''
|
|
230
|
+
].join('\n'));
|
|
231
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'PreviewPanel.svelte'), '<div class="preview-shell"></div>\n');
|
|
232
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts'), [
|
|
233
|
+
"export { overrideVariants } from './variants.ts';",
|
|
234
|
+
"export { default as PreviewOverride } from './PreviewOverride.svelte';",
|
|
235
|
+
''
|
|
236
|
+
].join('\n'));
|
|
237
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'variants.ts'), "export const overrideVariants = { class: 'override-variant border-primary/80' };\n");
|
|
238
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte'), '<div class="override-flex px-2"></div>\n');
|
|
239
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'index.ts'), [
|
|
240
|
+
"export { projectCard } from './projectCard.svelte.ts';",
|
|
241
|
+
"export { sharedBadge } from '../../admin/dist/lib/shared.ts';",
|
|
242
|
+
''
|
|
243
|
+
].join('\n'));
|
|
244
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'projectCard.svelte.ts'), "export const projectCard = { class: 'project-card shadow-sm' };\n");
|
|
245
|
+
const scanner = createGraphScanner(packageRoot);
|
|
246
|
+
const adminEntry = join(packageRoot, 'admin', 'dist', 'lib', 'index.ts');
|
|
247
|
+
const overrideEntry = join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts');
|
|
248
|
+
const projectEntry = join(packageRoot, 'project', 'dist', 'index.ts');
|
|
249
|
+
const [adminScan, overrideScan, projectScan, adminTargets] = await withTimeout(Promise.all([
|
|
250
|
+
scanner.scanFileGraph([adminEntry]),
|
|
251
|
+
scanner.scanFileGraph([overrideEntry]),
|
|
252
|
+
scanner.scanFileGraph([projectEntry]),
|
|
253
|
+
scanner.readEntrySymbolTargets(adminEntry)
|
|
254
|
+
]));
|
|
255
|
+
expect([...adminScan.classes].sort()).toEqual([
|
|
256
|
+
'admin-grid',
|
|
257
|
+
'border-primary/80',
|
|
258
|
+
'gap-4',
|
|
259
|
+
'override-flex',
|
|
260
|
+
'override-variant',
|
|
261
|
+
'preview-shell',
|
|
262
|
+
'px-2',
|
|
263
|
+
'rounded-md',
|
|
264
|
+
'shared-badge'
|
|
265
|
+
]);
|
|
266
|
+
expect([...overrideScan.classes].sort()).toEqual([
|
|
267
|
+
'border-primary/80',
|
|
268
|
+
'override-flex',
|
|
269
|
+
'override-variant',
|
|
270
|
+
'px-2'
|
|
271
|
+
]);
|
|
272
|
+
expect([...projectScan.classes].sort()).toEqual(['project-card', 'rounded-md', 'shadow-sm', 'shared-badge']);
|
|
273
|
+
expect(adminTargets.get('PreviewOverride')).toBe(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte'));
|
|
274
|
+
expect(adminTargets.get('sharedBadge')).toBe(join(packageRoot, 'admin', 'dist', 'lib', 'shared.ts'));
|
|
275
|
+
});
|
|
276
|
+
it('preserves all overlapping exports during package manifest generation', async () => {
|
|
277
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-overlapping-exports-');
|
|
278
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
279
|
+
await mkdir(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides'), { recursive: true });
|
|
280
|
+
await mkdir(join(packageRoot, 'project', 'dist'), { recursive: true });
|
|
281
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
282
|
+
name: 'package-a',
|
|
283
|
+
private: true,
|
|
284
|
+
type: 'module',
|
|
285
|
+
exports: {
|
|
286
|
+
'./admin': './admin/dist/lib/index.ts',
|
|
287
|
+
'./admin/overrides': './admin/dist/lib/components/preview/overrides/index.ts',
|
|
288
|
+
'./project': './project/dist/index.ts'
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'index.ts'), [
|
|
292
|
+
"export * from './components/preview/index.ts';",
|
|
293
|
+
"export { default as AdminPanel } from './components/AdminPanel.svelte';",
|
|
294
|
+
''
|
|
295
|
+
].join('\n'));
|
|
296
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'AdminPanel.svelte'), '<div class="admin-grid gap-4"></div>\n');
|
|
297
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'index.ts'), [
|
|
298
|
+
"export { default as PreviewPanel } from './PreviewPanel.svelte';",
|
|
299
|
+
"export * from './overrides/index.ts';",
|
|
300
|
+
''
|
|
301
|
+
].join('\n'));
|
|
302
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'PreviewPanel.svelte'), '<div class="preview-shell"></div>\n');
|
|
303
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts'), [
|
|
304
|
+
"export { overrideVariants } from './variants.ts';",
|
|
305
|
+
"export { default as PreviewOverride } from './PreviewOverride.svelte';",
|
|
306
|
+
''
|
|
307
|
+
].join('\n'));
|
|
308
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'variants.ts'), "export const overrideVariants = { class: 'override-variant border-primary/80' };\n");
|
|
309
|
+
await writeFile(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte'), '<div class="override-flex px-2"></div>\n');
|
|
310
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'index.ts'), [
|
|
311
|
+
"export { default as ProjectPanel } from './ProjectPanel.svelte';",
|
|
312
|
+
"export { default as PreviewOverrideProject } from '../../admin/dist/lib/components/preview/overrides/PreviewOverride.svelte';",
|
|
313
|
+
''
|
|
314
|
+
].join('\n'));
|
|
315
|
+
await writeFile(join(packageRoot, 'project', 'dist', 'ProjectPanel.svelte'), '<div class="project-card shadow-sm"></div>\n');
|
|
316
|
+
const result = await withTimeout(generateTailwindManifestForPackage(packageRoot, { exportFilters: ['./admin', './project'] }));
|
|
317
|
+
expect(result.exportCount).toBe(3);
|
|
318
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
319
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
320
|
+
expect(Object.keys(manifest.exports)).toEqual(['./admin', './admin/overrides', './project']);
|
|
321
|
+
expect(manifest.exports['./admin']?.symbols?.PreviewOverride?.classes).toEqual(['override-flex', 'px-2']);
|
|
322
|
+
expect(manifest.exports['./admin/overrides']?.symbols?.overrideVariants?.classes).toEqual([
|
|
323
|
+
'border-primary/80',
|
|
324
|
+
'override-variant'
|
|
325
|
+
]);
|
|
326
|
+
expect(manifest.exports['./project']?.symbols?.PreviewOverrideProject?.classes).toEqual(['override-flex', 'px-2']);
|
|
327
|
+
});
|
|
328
|
+
it('ignores class examples inside svelte comments when collecting manifest classes', async () => {
|
|
329
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-svelte-comments-');
|
|
330
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
331
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
332
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
333
|
+
name: 'package-a',
|
|
334
|
+
private: true,
|
|
335
|
+
type: 'module',
|
|
336
|
+
exports: {
|
|
337
|
+
'./field': './dist/Field.svelte'
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
await writeFile(join(packageRoot, 'dist', 'Field.svelte'), [
|
|
341
|
+
'<!--',
|
|
342
|
+
'```svelte',
|
|
343
|
+
'<Field class="z-5 w-full self-start" />',
|
|
344
|
+
'```',
|
|
345
|
+
'-->',
|
|
346
|
+
'<script lang="ts">',
|
|
347
|
+
' let className = "";',
|
|
348
|
+
'</script>',
|
|
349
|
+
'<div class={className}></div>',
|
|
350
|
+
'<div class="self-start"></div>',
|
|
351
|
+
''
|
|
352
|
+
].join('\n'));
|
|
353
|
+
process.chdir(packageRoot);
|
|
354
|
+
await main([]);
|
|
355
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
356
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
357
|
+
expect(manifest.exports['./field']?.classes).toEqual(['self-start']);
|
|
358
|
+
});
|
|
359
|
+
it('collects any object properties whose names mention class from script files', async () => {
|
|
360
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-icon-props-');
|
|
361
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
362
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
363
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
364
|
+
name: 'package-a',
|
|
365
|
+
private: true,
|
|
366
|
+
type: 'module',
|
|
367
|
+
exports: {
|
|
368
|
+
'./admin': './dist/admin.ts'
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
await writeFile(join(packageRoot, 'dist', 'admin.ts'), [
|
|
372
|
+
"export const contrib = defineContrib('admin', {",
|
|
373
|
+
' headerItems: [',
|
|
374
|
+
" { title: 'Home', link: '/', iconClass: 'icon-[ic--round-home]' },",
|
|
375
|
+
" { title: 'Profile', link: '/profile/settings/', triggerClassName: 'icon-user icon-page' }",
|
|
376
|
+
' ]',
|
|
377
|
+
'});',
|
|
378
|
+
''
|
|
379
|
+
].join('\n'));
|
|
380
|
+
process.chdir(packageRoot);
|
|
381
|
+
await main([]);
|
|
382
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
383
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
384
|
+
expect(manifest.exports['./admin']?.classes).toEqual(['icon-[ic--round-home]', 'icon-page', 'icon-user']);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/lib/bin/build-tailwind-manifest/cli.ts"],"names":[],"mappings":"AAYA,wBAAsB,IAAI,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { glob, readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import { relative, join, resolve, basename, dirname } from 'node:path';
|
|
4
|
+
import { normalizeManifestExportFilter } from '../../vite/tailwind-sources-manifest.js';
|
|
5
|
+
import { generateTailwindManifestForPackage } from './manifest-generator.js';
|
|
6
|
+
import { toPosixPath } from './path-utils.js';
|
|
7
|
+
const IGNORED_DIRECTORIES = new Set(['.git', '.svelte-kit', 'node_modules']);
|
|
8
|
+
const WATCH_POLL_INTERVAL_MS = 700;
|
|
9
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
10
|
+
const options = parseCliArgs(argv);
|
|
11
|
+
const rootDir = process.cwd();
|
|
12
|
+
if (options.watch) {
|
|
13
|
+
await runWatch(rootDir, options);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
await runBuild(rootDir, options);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function runBuild(rootDir, options) {
|
|
20
|
+
const packageDirs = await discoverPackageDirectories(rootDir, options.packagePatterns);
|
|
21
|
+
if (packageDirs.length === 0) {
|
|
22
|
+
throw new Error('[tailwind-manifest] No matching package.json files found.');
|
|
23
|
+
}
|
|
24
|
+
await Promise.all(packageDirs.map((packageDir) => buildPackage(rootDir, packageDir, options.exportFilters)));
|
|
25
|
+
return packageDirs;
|
|
26
|
+
}
|
|
27
|
+
async function runWatch(rootDir, options) {
|
|
28
|
+
const packageDirs = await runBuild(rootDir, options);
|
|
29
|
+
const snapshots = new Map(await Promise.all(packageDirs.map(async (packageDir) => [packageDir, await createDirectorySnapshot(packageDir)])));
|
|
30
|
+
console.log(`[tailwind-manifest] watching ${packageDirs.length} package${packageDirs.length === 1 ? '' : 's'}`);
|
|
31
|
+
const interval = setInterval(async () => {
|
|
32
|
+
for (const packageDir of packageDirs) {
|
|
33
|
+
const nextSnapshot = await createDirectorySnapshot(packageDir);
|
|
34
|
+
if (snapshots.get(packageDir) === nextSnapshot)
|
|
35
|
+
continue;
|
|
36
|
+
snapshots.set(packageDir, nextSnapshot);
|
|
37
|
+
try {
|
|
38
|
+
await buildPackage(rootDir, packageDir, options.exportFilters);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error(error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}, WATCH_POLL_INTERVAL_MS);
|
|
45
|
+
process.on('SIGINT', () => {
|
|
46
|
+
clearInterval(interval);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
await new Promise(() => { });
|
|
50
|
+
}
|
|
51
|
+
function parseCliArgs(argv) {
|
|
52
|
+
const packagePatterns = [];
|
|
53
|
+
const exportFilters = [];
|
|
54
|
+
let watch = false;
|
|
55
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
56
|
+
const arg = argv[i];
|
|
57
|
+
const value = argv[i + 1];
|
|
58
|
+
if (arg === '--watch') {
|
|
59
|
+
watch = true;
|
|
60
|
+
}
|
|
61
|
+
else if (arg === '--packages') {
|
|
62
|
+
if (!value)
|
|
63
|
+
throw new Error('--packages requires a value');
|
|
64
|
+
packagePatterns.push(...value
|
|
65
|
+
.split(',')
|
|
66
|
+
.map((part) => part.trim())
|
|
67
|
+
.filter(Boolean));
|
|
68
|
+
i += 1;
|
|
69
|
+
}
|
|
70
|
+
else if (arg === '--exports') {
|
|
71
|
+
if (!value)
|
|
72
|
+
throw new Error('--exports requires a value');
|
|
73
|
+
exportFilters.push(...value.split(',').map(normalizeManifestExportFilter).filter(Boolean));
|
|
74
|
+
i += 1;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { exportFilters, packagePatterns, watch };
|
|
81
|
+
}
|
|
82
|
+
async function discoverPackageDirectories(rootDir, packagePatterns) {
|
|
83
|
+
if (packagePatterns.length === 0 && existsSync(join(rootDir, 'package.json'))) {
|
|
84
|
+
return [rootDir];
|
|
85
|
+
}
|
|
86
|
+
const packageJsonPaths = new Set();
|
|
87
|
+
for (const pattern of packagePatterns) {
|
|
88
|
+
for await (const matchedPath of glob(pattern, { cwd: rootDir })) {
|
|
89
|
+
const absolutePath = resolve(rootDir, matchedPath);
|
|
90
|
+
if (basename(absolutePath) === 'package.json') {
|
|
91
|
+
packageJsonPaths.add(absolutePath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return [...packageJsonPaths].map((packageJsonPath) => dirname(packageJsonPath)).sort();
|
|
96
|
+
}
|
|
97
|
+
async function buildPackage(rootDir, packageDir, exportFilters) {
|
|
98
|
+
const result = await generateTailwindManifestForPackage(packageDir, { exportFilters });
|
|
99
|
+
const relativeOutputPath = relative(rootDir, result.outputFile) || result.outputFile;
|
|
100
|
+
console.log(`[tailwind-manifest] ${result.didWrite ? 'wrote' : 'unchanged'} ${relativeOutputPath} (${result.exportCount} exports)`);
|
|
101
|
+
}
|
|
102
|
+
async function createDirectorySnapshot(packageDir) {
|
|
103
|
+
const files = [];
|
|
104
|
+
const queue = [packageDir];
|
|
105
|
+
while (queue.length > 0) {
|
|
106
|
+
const currentDir = queue.shift();
|
|
107
|
+
if (!currentDir)
|
|
108
|
+
break;
|
|
109
|
+
for (const entry of await readdir(currentDir, { withFileTypes: true })) {
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
if (!IGNORED_DIRECTORIES.has(entry.name)) {
|
|
112
|
+
queue.push(join(currentDir, entry.name));
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const absolutePath = join(currentDir, entry.name);
|
|
117
|
+
const fileStat = await stat(absolutePath);
|
|
118
|
+
files.push(`${toPosixPath(relative(packageDir, absolutePath))}:${fileStat.size}:${fileStat.mtimeMs}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return files.sort().join('|');
|
|
122
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GraphScan } from './types.js';
|
|
2
|
+
export type GraphScanner = {
|
|
3
|
+
scanFileGraph(entryFiles: string[]): Promise<GraphScan>;
|
|
4
|
+
readEntrySymbolTargets(entryFile: string): Promise<Map<string, string>>;
|
|
5
|
+
};
|
|
6
|
+
export declare function createGraphScanner(packageDir: string): GraphScanner;
|
|
7
|
+
export declare function readEntrySymbolTargets(entryFile: string, visited?: Set<string>): Promise<Map<string, string>>;
|
|
8
|
+
export declare function scanFileGraph(entryFiles: string[], packageDir: string): Promise<GraphScan>;
|
|
9
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/lib/bin/build-tailwind-manifest/graph.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA2B5C,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzE,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAgNnE;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,OAAO,cAAoB,GAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAM9B;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAEhG"}
|