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,591 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { parseTailwindSourceManifest } from '../../vite/tailwind-sources-manifest.js';
|
|
8
|
+
import { main } from './cli.js';
|
|
9
|
+
import { createGraphScanner } from './graph.js';
|
|
10
|
+
import { generateTailwindManifestForPackage } from './manifest-generator.js';
|
|
11
|
+
|
|
12
|
+
const tempDirectories: string[] = [];
|
|
13
|
+
const originalWorkingDirectory = process.cwd();
|
|
14
|
+
|
|
15
|
+
async function createTempDirectory(prefix: string): Promise<string> {
|
|
16
|
+
const directory = await mkdtemp(join(tmpdir(), prefix));
|
|
17
|
+
tempDirectories.push(directory);
|
|
18
|
+
return directory;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function writeJson(filePath: string, value: unknown): Promise<void> {
|
|
22
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function withTimeout<T>(promise: Promise<T>, timeoutMs = 5000): Promise<T> {
|
|
26
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
return await Promise.race([
|
|
30
|
+
promise,
|
|
31
|
+
new Promise<never>((_, reject) => {
|
|
32
|
+
timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
33
|
+
})
|
|
34
|
+
]);
|
|
35
|
+
} finally {
|
|
36
|
+
if (timer) clearTimeout(timer);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('build-tailwind-manifest', () => {
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
process.chdir(originalWorkingDirectory);
|
|
43
|
+
|
|
44
|
+
await Promise.all(
|
|
45
|
+
tempDirectories.splice(0).map((directory) =>
|
|
46
|
+
rm(directory, {
|
|
47
|
+
recursive: true,
|
|
48
|
+
force: true
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('generates manifests for matching package.json globs and filters export prefixes', async () => {
|
|
55
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-monorepo-');
|
|
56
|
+
const packageRoot = join(repoRoot, 'packages', 'module-a');
|
|
57
|
+
|
|
58
|
+
await mkdir(join(packageRoot, 'dist', 'admin', 'overrides'), { recursive: true });
|
|
59
|
+
await mkdir(join(packageRoot, 'dist', 'project'), { recursive: true });
|
|
60
|
+
await mkdir(join(packageRoot, 'testing', 'dist'), { recursive: true });
|
|
61
|
+
|
|
62
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
63
|
+
name: 'module-a',
|
|
64
|
+
private: true,
|
|
65
|
+
type: 'module',
|
|
66
|
+
exports: {
|
|
67
|
+
'./testing/*': './testing/dist/*',
|
|
68
|
+
'./admin': './dist/admin/index.ts',
|
|
69
|
+
'./admin/overrides': './dist/admin/overrides/index.ts',
|
|
70
|
+
'./project': './dist/project/index.ts'
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await writeFile(
|
|
75
|
+
join(packageRoot, 'dist', 'admin', 'index.ts'),
|
|
76
|
+
"export { default as AdminPanel } from './AdminPanel.svelte';\n"
|
|
77
|
+
);
|
|
78
|
+
await writeFile(join(packageRoot, 'dist', 'admin', 'AdminPanel.svelte'), '<div class="admin-grid gap-4"></div>\n');
|
|
79
|
+
await writeFile(
|
|
80
|
+
join(packageRoot, 'dist', 'admin', 'overrides', 'index.ts'),
|
|
81
|
+
"export { default as AdminOverride } from './AdminOverride.svelte';\n"
|
|
82
|
+
);
|
|
83
|
+
await writeFile(
|
|
84
|
+
join(packageRoot, 'dist', 'admin', 'overrides', 'AdminOverride.svelte'),
|
|
85
|
+
[
|
|
86
|
+
'<script module lang="ts">',
|
|
87
|
+
" export { overrideVariants } from './variants.ts';",
|
|
88
|
+
'</script>',
|
|
89
|
+
'<script lang="ts">',
|
|
90
|
+
" import { cn } from './util.js';",
|
|
91
|
+
' let { class: className } = $props();',
|
|
92
|
+
'</script>',
|
|
93
|
+
"<div class={cn('override-flex px-2', className)}></div>",
|
|
94
|
+
''
|
|
95
|
+
].join('\n')
|
|
96
|
+
);
|
|
97
|
+
await writeFile(
|
|
98
|
+
join(packageRoot, 'dist', 'admin', 'overrides', 'variants.ts'),
|
|
99
|
+
"export const overrideVariants = { class: 'override-variant rounded-md' };\n"
|
|
100
|
+
);
|
|
101
|
+
await writeFile(
|
|
102
|
+
join(packageRoot, 'dist', 'admin', 'overrides', 'util.js'),
|
|
103
|
+
"export const cn = (...values) => values.join(' ');\n"
|
|
104
|
+
);
|
|
105
|
+
await writeFile(
|
|
106
|
+
join(packageRoot, 'dist', 'project', 'index.ts'),
|
|
107
|
+
["import './theme.css';", "export { default as ProjectPanel } from './ProjectPanel.svelte';", ''].join('\n')
|
|
108
|
+
);
|
|
109
|
+
await writeFile(join(packageRoot, 'dist', 'project', 'ProjectPanel.svelte'), '<div class="project-root"></div>\n');
|
|
110
|
+
await writeFile(join(packageRoot, 'dist', 'project', 'theme.css'), '.project-theme { color: red; }\n');
|
|
111
|
+
await writeFile(join(packageRoot, 'testing', 'dist', 'ignored.ts'), 'export const ignored = true;\n');
|
|
112
|
+
|
|
113
|
+
process.chdir(repoRoot);
|
|
114
|
+
await main(['--packages', 'packages/*/package.json', '--exports', 'admin,project']);
|
|
115
|
+
|
|
116
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
117
|
+
const manifest = parseTailwindSourceManifest(manifestContents) as {
|
|
118
|
+
exports: Record<
|
|
119
|
+
string,
|
|
120
|
+
{
|
|
121
|
+
classes: string[];
|
|
122
|
+
sources: string[];
|
|
123
|
+
symbols?: Record<string, { classes: string[]; sources: string[] }>;
|
|
124
|
+
}
|
|
125
|
+
>;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
expect(manifestContents).toContain(
|
|
129
|
+
'/* tailwind-manifest export "./project"; styles from: ./dist/project/theme.css */'
|
|
130
|
+
);
|
|
131
|
+
expect(manifestContents).toContain('/* tailwind-manifest symbol "AdminPanel"; styles from: inline-only */');
|
|
132
|
+
expect(Object.keys(manifest.exports)).toEqual(['./admin', './admin/overrides', './project']);
|
|
133
|
+
expect(manifest.exports['./admin']?.classes).toEqual(['admin-grid', 'gap-4']);
|
|
134
|
+
expect(manifest.exports['./admin']?.symbols?.AdminPanel?.classes).toEqual(['admin-grid', 'gap-4']);
|
|
135
|
+
expect(manifest.exports['./admin/overrides']?.classes).toEqual([
|
|
136
|
+
'override-flex',
|
|
137
|
+
'override-variant',
|
|
138
|
+
'px-2',
|
|
139
|
+
'rounded-md'
|
|
140
|
+
]);
|
|
141
|
+
expect(manifest.exports['./project']?.classes).toEqual(['project-root']);
|
|
142
|
+
expect(manifest.exports['./project']?.sources).toEqual(['./dist/project/theme.css']);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('coerces legacy .json manifest overrides to .jsonc output', async () => {
|
|
146
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-json-override-');
|
|
147
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
148
|
+
|
|
149
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
150
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
151
|
+
name: 'package-a',
|
|
152
|
+
private: true,
|
|
153
|
+
type: 'module',
|
|
154
|
+
tailwindSources: './dist/custom-tailwind.manifest.json',
|
|
155
|
+
exports: {
|
|
156
|
+
'./panel': './dist/panel.svelte'
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
await writeFile(join(packageRoot, 'dist', 'panel.svelte'), '<div class="panel-root"></div>\n');
|
|
160
|
+
|
|
161
|
+
process.chdir(packageRoot);
|
|
162
|
+
await main([]);
|
|
163
|
+
|
|
164
|
+
const jsoncPath = join(packageRoot, 'dist', 'custom-tailwind.manifest.jsonc');
|
|
165
|
+
const manifestContents = await readFile(jsoncPath, 'utf8');
|
|
166
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
167
|
+
|
|
168
|
+
expect(manifest.exports['./panel']?.classes).toEqual(['panel-root']);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('handles root package exports declared as a string or conditional object', async () => {
|
|
172
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-root-exports-');
|
|
173
|
+
const stringExportRoot = join(repoRoot, 'packages', 'string-export');
|
|
174
|
+
const conditionalExportRoot = join(repoRoot, 'packages', 'conditional-export');
|
|
175
|
+
|
|
176
|
+
await mkdir(join(stringExportRoot, 'dist'), { recursive: true });
|
|
177
|
+
await mkdir(join(conditionalExportRoot, 'dist'), { recursive: true });
|
|
178
|
+
|
|
179
|
+
await writeJson(join(stringExportRoot, 'package.json'), {
|
|
180
|
+
name: 'string-export',
|
|
181
|
+
private: true,
|
|
182
|
+
type: 'module',
|
|
183
|
+
exports: './dist/index.ts'
|
|
184
|
+
});
|
|
185
|
+
await writeFile(join(stringExportRoot, 'dist', 'index.ts'), "export const rootCard = { class: 'root-card' };\n");
|
|
186
|
+
|
|
187
|
+
await writeJson(join(conditionalExportRoot, 'package.json'), {
|
|
188
|
+
name: 'conditional-export',
|
|
189
|
+
private: true,
|
|
190
|
+
type: 'module',
|
|
191
|
+
exports: {
|
|
192
|
+
types: './dist/index.d.ts',
|
|
193
|
+
import: './dist/index.ts'
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
await writeFile(
|
|
197
|
+
join(conditionalExportRoot, 'dist', 'index.ts'),
|
|
198
|
+
"export const conditionalCard = { class: 'conditional-card' };\n"
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
process.chdir(repoRoot);
|
|
202
|
+
await main(['--packages', 'packages/*/package.json', '--exports', '.']);
|
|
203
|
+
|
|
204
|
+
const stringManifest = parseTailwindSourceManifest(
|
|
205
|
+
await readFile(join(stringExportRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8')
|
|
206
|
+
);
|
|
207
|
+
const conditionalManifest = parseTailwindSourceManifest(
|
|
208
|
+
await readFile(join(conditionalExportRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8')
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(Object.keys(stringManifest.exports)).toEqual(['.']);
|
|
212
|
+
expect(stringManifest.exports['.']?.classes).toEqual(['root-card']);
|
|
213
|
+
expect(Object.keys(conditionalManifest.exports)).toEqual(['.']);
|
|
214
|
+
expect(conditionalManifest.exports['.']?.classes).toEqual(['conditional-card']);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('resolves emitted .js export specifiers back to .ts and .svelte.ts source files', async () => {
|
|
218
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-emitted-js-');
|
|
219
|
+
const packageRoot = join(repoRoot, 'packages', 'modules', 'contact');
|
|
220
|
+
|
|
221
|
+
await mkdir(join(packageRoot, 'project', 'dist', 'contact', 'components'), { recursive: true });
|
|
222
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
223
|
+
|
|
224
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
225
|
+
name: 'contact-module',
|
|
226
|
+
private: true,
|
|
227
|
+
type: 'module',
|
|
228
|
+
exports: {
|
|
229
|
+
'./project': './project/dist/index.ts'
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await writeFile(
|
|
234
|
+
join(packageRoot, 'project', 'dist', 'index.ts'),
|
|
235
|
+
[
|
|
236
|
+
"export { contactClientApiRequest, contactClientForm } from './api.svelte.js';",
|
|
237
|
+
"export * from './contact/index.js';",
|
|
238
|
+
"export * from './direct.js';",
|
|
239
|
+
''
|
|
240
|
+
].join('\n')
|
|
241
|
+
);
|
|
242
|
+
await writeFile(
|
|
243
|
+
join(packageRoot, 'project', 'dist', 'api.svelte.ts'),
|
|
244
|
+
"export const contactClientForm = { class: 'api-form-inline' };\n"
|
|
245
|
+
);
|
|
246
|
+
await writeFile(
|
|
247
|
+
join(packageRoot, 'project', 'dist', 'direct.ts'),
|
|
248
|
+
"export const contactChip = { class: 'contact-chip' };\n"
|
|
249
|
+
);
|
|
250
|
+
await writeFile(
|
|
251
|
+
join(packageRoot, 'project', 'dist', 'contact', 'index.ts'),
|
|
252
|
+
[
|
|
253
|
+
"export { default as Contact } from './components/Contact.svelte';",
|
|
254
|
+
"export { default as ContactField } from './components/ContactField.svelte';",
|
|
255
|
+
''
|
|
256
|
+
].join('\n')
|
|
257
|
+
);
|
|
258
|
+
await writeFile(
|
|
259
|
+
join(packageRoot, 'project', 'dist', 'contact', 'components', 'Contact.svelte'),
|
|
260
|
+
'<div class="flex flex-col items-center gap-2"></div>\n'
|
|
261
|
+
);
|
|
262
|
+
await writeFile(
|
|
263
|
+
join(packageRoot, 'project', 'dist', 'contact', 'components', 'ContactField.svelte'),
|
|
264
|
+
'<div class="self-start w-full z-5"></div>\n'
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
process.chdir(packageRoot);
|
|
268
|
+
await main([]);
|
|
269
|
+
|
|
270
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
271
|
+
const manifest = parseTailwindSourceManifest(manifestContents) as {
|
|
272
|
+
exports: Record<
|
|
273
|
+
string,
|
|
274
|
+
{
|
|
275
|
+
classes: string[];
|
|
276
|
+
sources: string[];
|
|
277
|
+
symbols?: Record<string, { classes: string[]; sources: string[] }>;
|
|
278
|
+
}
|
|
279
|
+
>;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
expect(manifest.exports['./project']?.classes).toEqual([
|
|
283
|
+
'api-form-inline',
|
|
284
|
+
'contact-chip',
|
|
285
|
+
'flex',
|
|
286
|
+
'flex-col',
|
|
287
|
+
'gap-2',
|
|
288
|
+
'items-center',
|
|
289
|
+
'self-start',
|
|
290
|
+
'w-full',
|
|
291
|
+
'z-5'
|
|
292
|
+
]);
|
|
293
|
+
expect(manifest.exports['./project']?.symbols?.contactChip?.classes).toEqual(['contact-chip']);
|
|
294
|
+
expect(manifest.exports['./project']?.symbols?.contactClientForm?.classes).toEqual(['api-form-inline']);
|
|
295
|
+
expect(manifest.exports['./project']?.symbols?.Contact?.classes).toEqual([
|
|
296
|
+
'flex',
|
|
297
|
+
'flex-col',
|
|
298
|
+
'gap-2',
|
|
299
|
+
'items-center'
|
|
300
|
+
]);
|
|
301
|
+
expect(manifest.exports['./project']?.symbols?.ContactField?.classes).toEqual(['self-start', 'w-full', 'z-5']);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('completes concurrent overlapping graph scans on a shared scanner', async () => {
|
|
305
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-concurrent-scanner-');
|
|
306
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
307
|
+
|
|
308
|
+
await mkdir(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides'), { recursive: true });
|
|
309
|
+
await mkdir(join(packageRoot, 'project', 'dist'), { recursive: true });
|
|
310
|
+
|
|
311
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
312
|
+
name: 'package-a',
|
|
313
|
+
private: true,
|
|
314
|
+
type: 'module',
|
|
315
|
+
exports: {
|
|
316
|
+
'./admin': './admin/dist/lib/index.ts',
|
|
317
|
+
'./admin/overrides': './admin/dist/lib/components/preview/overrides/index.ts',
|
|
318
|
+
'./project': './project/dist/index.ts'
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
await writeFile(
|
|
323
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'index.ts'),
|
|
324
|
+
[
|
|
325
|
+
"export { sharedBadge } from './shared.ts';",
|
|
326
|
+
"export * from './components/preview/index.ts';",
|
|
327
|
+
"export { default as AdminPanel } from './components/AdminPanel.svelte';",
|
|
328
|
+
''
|
|
329
|
+
].join('\n')
|
|
330
|
+
);
|
|
331
|
+
await writeFile(
|
|
332
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'shared.ts'),
|
|
333
|
+
"export const sharedBadge = { class: 'shared-badge rounded-md' };\n"
|
|
334
|
+
);
|
|
335
|
+
await writeFile(
|
|
336
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'AdminPanel.svelte'),
|
|
337
|
+
'<div class="admin-grid gap-4"></div>\n'
|
|
338
|
+
);
|
|
339
|
+
await writeFile(
|
|
340
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'index.ts'),
|
|
341
|
+
[
|
|
342
|
+
"export { default as PreviewPanel } from './PreviewPanel.svelte';",
|
|
343
|
+
"export * from './overrides/index.ts';",
|
|
344
|
+
''
|
|
345
|
+
].join('\n')
|
|
346
|
+
);
|
|
347
|
+
await writeFile(
|
|
348
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'PreviewPanel.svelte'),
|
|
349
|
+
'<div class="preview-shell"></div>\n'
|
|
350
|
+
);
|
|
351
|
+
await writeFile(
|
|
352
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts'),
|
|
353
|
+
[
|
|
354
|
+
"export { overrideVariants } from './variants.ts';",
|
|
355
|
+
"export { default as PreviewOverride } from './PreviewOverride.svelte';",
|
|
356
|
+
''
|
|
357
|
+
].join('\n')
|
|
358
|
+
);
|
|
359
|
+
await writeFile(
|
|
360
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'variants.ts'),
|
|
361
|
+
"export const overrideVariants = { class: 'override-variant border-primary/80' };\n"
|
|
362
|
+
);
|
|
363
|
+
await writeFile(
|
|
364
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte'),
|
|
365
|
+
'<div class="override-flex px-2"></div>\n'
|
|
366
|
+
);
|
|
367
|
+
await writeFile(
|
|
368
|
+
join(packageRoot, 'project', 'dist', 'index.ts'),
|
|
369
|
+
[
|
|
370
|
+
"export { projectCard } from './projectCard.svelte.ts';",
|
|
371
|
+
"export { sharedBadge } from '../../admin/dist/lib/shared.ts';",
|
|
372
|
+
''
|
|
373
|
+
].join('\n')
|
|
374
|
+
);
|
|
375
|
+
await writeFile(
|
|
376
|
+
join(packageRoot, 'project', 'dist', 'projectCard.svelte.ts'),
|
|
377
|
+
"export const projectCard = { class: 'project-card shadow-sm' };\n"
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const scanner = createGraphScanner(packageRoot);
|
|
381
|
+
const adminEntry = join(packageRoot, 'admin', 'dist', 'lib', 'index.ts');
|
|
382
|
+
const overrideEntry = join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts');
|
|
383
|
+
const projectEntry = join(packageRoot, 'project', 'dist', 'index.ts');
|
|
384
|
+
|
|
385
|
+
const [adminScan, overrideScan, projectScan, adminTargets] = await withTimeout(
|
|
386
|
+
Promise.all([
|
|
387
|
+
scanner.scanFileGraph([adminEntry]),
|
|
388
|
+
scanner.scanFileGraph([overrideEntry]),
|
|
389
|
+
scanner.scanFileGraph([projectEntry]),
|
|
390
|
+
scanner.readEntrySymbolTargets(adminEntry)
|
|
391
|
+
])
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
expect([...adminScan.classes].sort()).toEqual([
|
|
395
|
+
'admin-grid',
|
|
396
|
+
'border-primary/80',
|
|
397
|
+
'gap-4',
|
|
398
|
+
'override-flex',
|
|
399
|
+
'override-variant',
|
|
400
|
+
'preview-shell',
|
|
401
|
+
'px-2',
|
|
402
|
+
'rounded-md',
|
|
403
|
+
'shared-badge'
|
|
404
|
+
]);
|
|
405
|
+
expect([...overrideScan.classes].sort()).toEqual([
|
|
406
|
+
'border-primary/80',
|
|
407
|
+
'override-flex',
|
|
408
|
+
'override-variant',
|
|
409
|
+
'px-2'
|
|
410
|
+
]);
|
|
411
|
+
expect([...projectScan.classes].sort()).toEqual(['project-card', 'rounded-md', 'shadow-sm', 'shared-badge']);
|
|
412
|
+
expect(adminTargets.get('PreviewOverride')).toBe(
|
|
413
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte')
|
|
414
|
+
);
|
|
415
|
+
expect(adminTargets.get('sharedBadge')).toBe(join(packageRoot, 'admin', 'dist', 'lib', 'shared.ts'));
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('preserves all overlapping exports during package manifest generation', async () => {
|
|
419
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-overlapping-exports-');
|
|
420
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
421
|
+
|
|
422
|
+
await mkdir(join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides'), { recursive: true });
|
|
423
|
+
await mkdir(join(packageRoot, 'project', 'dist'), { recursive: true });
|
|
424
|
+
|
|
425
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
426
|
+
name: 'package-a',
|
|
427
|
+
private: true,
|
|
428
|
+
type: 'module',
|
|
429
|
+
exports: {
|
|
430
|
+
'./admin': './admin/dist/lib/index.ts',
|
|
431
|
+
'./admin/overrides': './admin/dist/lib/components/preview/overrides/index.ts',
|
|
432
|
+
'./project': './project/dist/index.ts'
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
await writeFile(
|
|
437
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'index.ts'),
|
|
438
|
+
[
|
|
439
|
+
"export * from './components/preview/index.ts';",
|
|
440
|
+
"export { default as AdminPanel } from './components/AdminPanel.svelte';",
|
|
441
|
+
''
|
|
442
|
+
].join('\n')
|
|
443
|
+
);
|
|
444
|
+
await writeFile(
|
|
445
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'AdminPanel.svelte'),
|
|
446
|
+
'<div class="admin-grid gap-4"></div>\n'
|
|
447
|
+
);
|
|
448
|
+
await writeFile(
|
|
449
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'index.ts'),
|
|
450
|
+
[
|
|
451
|
+
"export { default as PreviewPanel } from './PreviewPanel.svelte';",
|
|
452
|
+
"export * from './overrides/index.ts';",
|
|
453
|
+
''
|
|
454
|
+
].join('\n')
|
|
455
|
+
);
|
|
456
|
+
await writeFile(
|
|
457
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'PreviewPanel.svelte'),
|
|
458
|
+
'<div class="preview-shell"></div>\n'
|
|
459
|
+
);
|
|
460
|
+
await writeFile(
|
|
461
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'index.ts'),
|
|
462
|
+
[
|
|
463
|
+
"export { overrideVariants } from './variants.ts';",
|
|
464
|
+
"export { default as PreviewOverride } from './PreviewOverride.svelte';",
|
|
465
|
+
''
|
|
466
|
+
].join('\n')
|
|
467
|
+
);
|
|
468
|
+
await writeFile(
|
|
469
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'variants.ts'),
|
|
470
|
+
"export const overrideVariants = { class: 'override-variant border-primary/80' };\n"
|
|
471
|
+
);
|
|
472
|
+
await writeFile(
|
|
473
|
+
join(packageRoot, 'admin', 'dist', 'lib', 'components', 'preview', 'overrides', 'PreviewOverride.svelte'),
|
|
474
|
+
'<div class="override-flex px-2"></div>\n'
|
|
475
|
+
);
|
|
476
|
+
await writeFile(
|
|
477
|
+
join(packageRoot, 'project', 'dist', 'index.ts'),
|
|
478
|
+
[
|
|
479
|
+
"export { default as ProjectPanel } from './ProjectPanel.svelte';",
|
|
480
|
+
"export { default as PreviewOverrideProject } from '../../admin/dist/lib/components/preview/overrides/PreviewOverride.svelte';",
|
|
481
|
+
''
|
|
482
|
+
].join('\n')
|
|
483
|
+
);
|
|
484
|
+
await writeFile(
|
|
485
|
+
join(packageRoot, 'project', 'dist', 'ProjectPanel.svelte'),
|
|
486
|
+
'<div class="project-card shadow-sm"></div>\n'
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
const result = await withTimeout(
|
|
490
|
+
generateTailwindManifestForPackage(packageRoot, { exportFilters: ['./admin', './project'] })
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
expect(result.exportCount).toBe(3);
|
|
494
|
+
|
|
495
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
496
|
+
const manifest = parseTailwindSourceManifest(manifestContents) as {
|
|
497
|
+
exports: Record<
|
|
498
|
+
string,
|
|
499
|
+
{
|
|
500
|
+
classes: string[];
|
|
501
|
+
sources: string[];
|
|
502
|
+
symbols?: Record<string, { classes: string[]; sources: string[] }>;
|
|
503
|
+
}
|
|
504
|
+
>;
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
expect(Object.keys(manifest.exports)).toEqual(['./admin', './admin/overrides', './project']);
|
|
508
|
+
expect(manifest.exports['./admin']?.symbols?.PreviewOverride?.classes).toEqual(['override-flex', 'px-2']);
|
|
509
|
+
expect(manifest.exports['./admin/overrides']?.symbols?.overrideVariants?.classes).toEqual([
|
|
510
|
+
'border-primary/80',
|
|
511
|
+
'override-variant'
|
|
512
|
+
]);
|
|
513
|
+
expect(manifest.exports['./project']?.symbols?.PreviewOverrideProject?.classes).toEqual(['override-flex', 'px-2']);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('ignores class examples inside svelte comments when collecting manifest classes', async () => {
|
|
517
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-svelte-comments-');
|
|
518
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
519
|
+
|
|
520
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
521
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
522
|
+
name: 'package-a',
|
|
523
|
+
private: true,
|
|
524
|
+
type: 'module',
|
|
525
|
+
exports: {
|
|
526
|
+
'./field': './dist/Field.svelte'
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
await writeFile(
|
|
531
|
+
join(packageRoot, 'dist', 'Field.svelte'),
|
|
532
|
+
[
|
|
533
|
+
'<!--',
|
|
534
|
+
'```svelte',
|
|
535
|
+
'<Field class="z-5 w-full self-start" />',
|
|
536
|
+
'```',
|
|
537
|
+
'-->',
|
|
538
|
+
'<script lang="ts">',
|
|
539
|
+
' let className = "";',
|
|
540
|
+
'</script>',
|
|
541
|
+
'<div class={className}></div>',
|
|
542
|
+
'<div class="self-start"></div>',
|
|
543
|
+
''
|
|
544
|
+
].join('\n')
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
process.chdir(packageRoot);
|
|
548
|
+
await main([]);
|
|
549
|
+
|
|
550
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
551
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
552
|
+
|
|
553
|
+
expect(manifest.exports['./field']?.classes).toEqual(['self-start']);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('collects any object properties whose names mention class from script files', async () => {
|
|
557
|
+
const repoRoot = await createTempDirectory('tailwind-manifest-icon-props-');
|
|
558
|
+
const packageRoot = join(repoRoot, 'package-a');
|
|
559
|
+
|
|
560
|
+
await mkdir(join(packageRoot, 'dist'), { recursive: true });
|
|
561
|
+
await writeJson(join(packageRoot, 'package.json'), {
|
|
562
|
+
name: 'package-a',
|
|
563
|
+
private: true,
|
|
564
|
+
type: 'module',
|
|
565
|
+
exports: {
|
|
566
|
+
'./admin': './dist/admin.ts'
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
await writeFile(
|
|
571
|
+
join(packageRoot, 'dist', 'admin.ts'),
|
|
572
|
+
[
|
|
573
|
+
"export const contrib = defineContrib('admin', {",
|
|
574
|
+
' headerItems: [',
|
|
575
|
+
" { title: 'Home', link: '/', iconClass: 'icon-[ic--round-home]' },",
|
|
576
|
+
" { title: 'Profile', link: '/profile/settings/', triggerClassName: 'icon-user icon-page' }",
|
|
577
|
+
' ]',
|
|
578
|
+
'});',
|
|
579
|
+
''
|
|
580
|
+
].join('\n')
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
process.chdir(packageRoot);
|
|
584
|
+
await main([]);
|
|
585
|
+
|
|
586
|
+
const manifestContents = await readFile(join(packageRoot, 'dist', 'tailwind-sources.manifest.jsonc'), 'utf8');
|
|
587
|
+
const manifest = parseTailwindSourceManifest(manifestContents);
|
|
588
|
+
|
|
589
|
+
expect(manifest.exports['./admin']?.classes).toEqual(['icon-[ic--round-home]', 'icon-page', 'icon-user']);
|
|
590
|
+
});
|
|
591
|
+
});
|