netjes 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/cli/info.mjs +268 -31
- package/cli/routes.mjs +35 -8
- package/dist/href.d.ts +7 -0
- package/dist/href.d.ts.map +1 -1
- package/dist/href.js +16 -5
- package/dist/href.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mounted.d.ts +55 -0
- package/dist/mounted.d.ts.map +1 -0
- package/dist/mounted.js +110 -0
- package/dist/mounted.js.map +1 -0
- package/dist/payload.d.ts +8 -0
- package/dist/payload.d.ts.map +1 -1
- package/dist/payload.js +28 -0
- package/dist/payload.js.map +1 -1
- package/dist/render-document.d.ts +18 -4
- package/dist/render-document.d.ts.map +1 -1
- package/dist/render-document.js +79 -16
- package/dist/render-document.js.map +1 -1
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/sidecar/server.mjs +37 -10
- package/skills/netjes/SKILL.md +4 -3
- package/skills/netjes/references/data-and-actions.md +6 -2
- package/skills/netjes/references/routing.md +14 -3
- package/src/href.ts +16 -5
- package/src/index.ts +18 -1
- package/src/mounted.tsx +160 -0
- package/src/payload.ts +32 -0
- package/src/render-document.tsx +117 -20
- package/src/testing.tsx +7 -0
- package/src/virtual.d.ts +6 -1
- package/template/_AGENTS.md +4 -3
- package/vite/packages.mjs +345 -0
- package/vite/plugin.d.ts +7 -0
- package/vite/plugin.mjs +208 -30
package/vite/plugin.mjs
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import {
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { copyFile, mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
3
4
|
import { createRequire } from 'node:module';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import react from '@vitejs/plugin-react';
|
|
7
8
|
import { normalizePath, searchForWorkspaceRoot } from 'vite';
|
|
9
|
+
import { findPackage } from '../cli/packages.mjs';
|
|
10
|
+
import {
|
|
11
|
+
CONVENTION_EXTENSIONS,
|
|
12
|
+
CONVENTION_FILES,
|
|
13
|
+
commonJsDependencies,
|
|
14
|
+
fileId,
|
|
15
|
+
filePath,
|
|
16
|
+
isConventionFile,
|
|
17
|
+
isPackageName,
|
|
18
|
+
pagePackageFiles,
|
|
19
|
+
resolvePagePackages,
|
|
20
|
+
unsupportedNetjes,
|
|
21
|
+
withPackageStyles,
|
|
22
|
+
} from './packages.mjs';
|
|
8
23
|
|
|
9
24
|
const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));
|
|
25
|
+
/** The version of this netjes, the app's: what a page package's peer range has to hold. */
|
|
26
|
+
const VERSION = /** @type {{ version: string }} */ (JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))).version;
|
|
10
27
|
// The compiled modules the package exports (npm run build writes them), so the app runs the code it type-checked, and
|
|
11
28
|
// gets one copy of each module: the same files the app's own imports of netjes resolve to. Vite names a file by its path
|
|
12
29
|
// with forward slashes, on Windows too, so these are named that way.
|
|
@@ -22,6 +39,13 @@ const PAGES_ID = 'virtual:netjes/pages';
|
|
|
22
39
|
const RESOLVED_PAGES_ID = `\0${PAGES_ID}`;
|
|
23
40
|
const QUERY_ID = 'virtual:netjes/query';
|
|
24
41
|
const RESOLVED_QUERY_ID = `\0${QUERY_ID}`;
|
|
42
|
+
/**
|
|
43
|
+
* A module @tailwindcss/vite builds, by its rule: a `.css` file, unless it is imported as text, as a URL or as a worker,
|
|
44
|
+
* which by the time a plugin transforms it is a JavaScript module that exports the text.
|
|
45
|
+
* @param {string} id
|
|
46
|
+
*/
|
|
47
|
+
const isTailwindStylesheet = (id) =>
|
|
48
|
+
!/[?&](?:worker|sharedworker|raw|url)\b|\?commonjs-proxy/.test(id) && !id.includes('/.vite/') && (id.split('?')[0] ?? id).endsWith('.css');
|
|
25
49
|
/** The URL the .NET dev asset resolver points browsers at. */
|
|
26
50
|
const DEV_CLIENT_ENTRY_URL = '/@netjes/client-entry';
|
|
27
51
|
|
|
@@ -40,10 +64,24 @@ const BACKEND_DIRS = ['bin', 'obj', 'dist/server'];
|
|
|
40
64
|
* @param {string} workspaceRoot
|
|
41
65
|
*/
|
|
42
66
|
function backendDeny(workspaceRoot) {
|
|
43
|
-
|
|
44
|
-
return [...BACKEND_FILES, ...BACKEND_DIRS.map((dir) => `${root}/**/${dir}/**`)];
|
|
67
|
+
return [...BACKEND_FILES, ...backendDirs(workspaceRoot)];
|
|
45
68
|
}
|
|
46
69
|
|
|
70
|
+
/**
|
|
71
|
+
* The backend folders below a folder, such as a page package's own `bin/` and `obj/`.
|
|
72
|
+
* @param {string} dir
|
|
73
|
+
*/
|
|
74
|
+
function backendDirs(dir) {
|
|
75
|
+
const root = normalizePath(dir).replace(/[()[\]{}!+@*?\\]/g, '\\$&');
|
|
76
|
+
return BACKEND_DIRS.map((folder) => `${root}/**/${folder}/**`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A path as a glob that matches it alone: the characters a glob reads, escaped as Vite's glob (tinyglobby) escapes them.
|
|
81
|
+
* @param {string} file With `/` separators.
|
|
82
|
+
*/
|
|
83
|
+
const literalGlob = (file) => file.replace(/[()[\]{}*?|]|[!+@](?=\()/g, '\\$&');
|
|
84
|
+
|
|
47
85
|
/** Loads @tailwindcss/vite only when Tailwind is on, so apps that turn it off need not install it. */
|
|
48
86
|
async function tailwind() {
|
|
49
87
|
try {
|
|
@@ -161,7 +199,11 @@ async function findRouteHandler(root, appDir) {
|
|
|
161
199
|
export default function netjes(options = {}) {
|
|
162
200
|
const appDir = (options.appDir ?? 'app').replace(/^\/+|\/+$/g, '');
|
|
163
201
|
// Only the convention files are routes; everything else in app/ is colocated code.
|
|
164
|
-
const appFiles = `/${appDir}/**/{
|
|
202
|
+
const appFiles = `/${appDir}/**/{${CONVENTION_FILES.join(',')}}.{${CONVENTION_EXTENSIONS.map((ext) => ext.slice(1)).join(',')}}`;
|
|
203
|
+
const packageNames = options.packages ?? [];
|
|
204
|
+
if (packageNames.length > 0 && options.tailwind === false) {
|
|
205
|
+
throw new Error('netjes: page packages style their pages with Tailwind CSS, which netjes({ tailwind: false }) turns off: turn it on, or list no packages');
|
|
206
|
+
}
|
|
165
207
|
const basePath = (options.basePath ?? '').replace(/\/+$/, '');
|
|
166
208
|
const query = options.query === false ? null : options.query === true || options.query === undefined ? {} : options.query;
|
|
167
209
|
// Fail when the config loads, not in the browser.
|
|
@@ -174,6 +216,47 @@ export default function netjes(options = {}) {
|
|
|
174
216
|
let outDir = '';
|
|
175
217
|
/** @type {import('vite').ViteDevServer | undefined} */
|
|
176
218
|
let devServer;
|
|
219
|
+
/** The page packages the app lists, read when the config is. @type {import('./packages.mjs').PagePackage[]} */
|
|
220
|
+
let packages = [];
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Has the next render, and the next page the browser loads, build the page module map again from the files on disk,
|
|
224
|
+
* as Vite does once its watcher reports a page file added or removed.
|
|
225
|
+
*/
|
|
226
|
+
function rescanPages() {
|
|
227
|
+
const timestamp = Date.now();
|
|
228
|
+
for (const { moduleGraph } of Object.values(devServer?.environments ?? {})) {
|
|
229
|
+
const pages = moduleGraph.getModuleById(RESOLVED_PAGES_ID);
|
|
230
|
+
if (pages) moduleGraph.invalidateModule(pages, new Set(), timestamp, true);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The listed package a file id is of, with the file's path in its folder of pages.
|
|
236
|
+
* @param {string} id
|
|
237
|
+
*/
|
|
238
|
+
function packageFile(id) {
|
|
239
|
+
for (const pkg of packages) {
|
|
240
|
+
const prefix = `${pkg.name}/${pkg.pages}/`;
|
|
241
|
+
if (id.startsWith(prefix)) return { pkg, file: id.slice(prefix.length) };
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The name of the installed package whose file a file id names, when the app does not list it in netjes({ packages }):
|
|
248
|
+
* the .NET app mounts it, and the page module map does not have it.
|
|
249
|
+
* @param {string} id
|
|
250
|
+
* @returns {string | null}
|
|
251
|
+
*/
|
|
252
|
+
function unlistedPackage(id) {
|
|
253
|
+
if (id.startsWith(`${appDir}/`) || packageFile(id)) return null;
|
|
254
|
+
const segments = id.split('/');
|
|
255
|
+
const length = id.startsWith('@') ? 2 : 1;
|
|
256
|
+
if (segments.length <= length) return null;
|
|
257
|
+
const name = segments.slice(0, length).join('/');
|
|
258
|
+
return isPackageName(name) && findPackage(rootDir, name) ? name : null;
|
|
259
|
+
}
|
|
177
260
|
|
|
178
261
|
/** @type {import('vite').Plugin} */
|
|
179
262
|
const plugin = {
|
|
@@ -185,6 +268,13 @@ export default function netjes(options = {}) {
|
|
|
185
268
|
const userTest = /** @type {{ test?: Record<string, unknown> }} */ (/** @type {unknown} */ (config)).test;
|
|
186
269
|
const root = path.resolve(config.root ?? process.cwd());
|
|
187
270
|
const workspaceRoot = searchForWorkspaceRoot(root);
|
|
271
|
+
packages = resolvePagePackages(root, packageNames, appDir);
|
|
272
|
+
// A build stops here, and the development sidecar reports it as Vite failing to start.
|
|
273
|
+
for (const pkg of packages) {
|
|
274
|
+
const unsupported = unsupportedNetjes(pkg, VERSION);
|
|
275
|
+
if (unsupported) throw new Error(`netjes: ${unsupported}`);
|
|
276
|
+
}
|
|
277
|
+
const listed = packages.map((pkg) => pkg.name);
|
|
188
278
|
const test = env.command === 'serve' && options.test !== false ? { test: testDefaults(userTest, root, query) } : {};
|
|
189
279
|
// Built asset URLs live under the base path; in development the browser loads modules from the Vite server's root.
|
|
190
280
|
const base = env.command === 'build' && basePath ? { base: `${basePath}/` } : {};
|
|
@@ -200,21 +290,30 @@ export default function netjes(options = {}) {
|
|
|
200
290
|
...(query ? ['@tanstack/react-query'] : []),
|
|
201
291
|
// The test setup's cleanup must unmount what the app's tests rendered.
|
|
202
292
|
...(env.mode === 'test' ? ['@testing-library/react'] : []),
|
|
293
|
+
// A page package's pages import netjes and zod, which a linked package would otherwise resolve from its own
|
|
294
|
+
// folder: a second copy, or none at all for one linked from outside the workspace.
|
|
295
|
+
...(packages.length > 0 ? ['netjes', ...(canResolve(root, 'zod') ? ['zod'] : [])] : []),
|
|
203
296
|
],
|
|
204
297
|
},
|
|
205
298
|
// netjes serves its own modules (client entry, runtime) and, for their source maps, its sources; a linked
|
|
206
|
-
// package lives outside the app
|
|
207
|
-
// fs.allow itself: Vite concatenates arrays. The dev root is
|
|
208
|
-
//
|
|
299
|
+
// package lives outside the app, and so does a page package linked from elsewhere. The workspace root keeps
|
|
300
|
+
// Vite's default, but only when the app did not narrow fs.allow itself: Vite concatenates arrays. The dev root is
|
|
301
|
+
// the .NET content root, so deny the backend's files, and a page package's backend folders wherever it is (its
|
|
302
|
+
// C# half's build output, in a checkout linked from outside the workspace). A configured deny list replaces
|
|
303
|
+
// Vite's defaults, which are repeated here.
|
|
209
304
|
server: {
|
|
210
305
|
fs: {
|
|
211
|
-
allow: [...(config.server?.fs?.allow ? [] : [workspaceRoot]), PACKAGE_ROOT],
|
|
212
|
-
deny: [...VITE_DENY, ...backendDeny(workspaceRoot)],
|
|
306
|
+
allow: [...(config.server?.fs?.allow ? [] : [workspaceRoot]), PACKAGE_ROOT, ...packages.map((pkg) => pkg.root)],
|
|
307
|
+
deny: [...VITE_DENY, ...backendDeny(workspaceRoot), ...packages.flatMap((pkg) => backendDirs(pkg.root))],
|
|
213
308
|
},
|
|
214
309
|
},
|
|
215
310
|
optimizeDeps: {
|
|
216
|
-
// There is no index.html to scan; start from the pages so dependencies are pre-bundled up front
|
|
217
|
-
|
|
311
|
+
// There is no index.html to scan; start from the pages so dependencies are pre-bundled up front, a page
|
|
312
|
+
// package's included: Vite globs these from the root, in node_modules and outside the root too.
|
|
313
|
+
entries: [
|
|
314
|
+
appFiles.slice(1),
|
|
315
|
+
...packages.map((pkg) => `${literalGlob(normalizePath(path.relative(root, `${pkg.root}/${pkg.pages}`)))}/${appFiles.slice(appDir.length + 2)}`),
|
|
316
|
+
],
|
|
218
317
|
include: [
|
|
219
318
|
'react',
|
|
220
319
|
'react-dom',
|
|
@@ -222,14 +321,19 @@ export default function netjes(options = {}) {
|
|
|
222
321
|
...(query ? ['@tanstack/react-query'] : []),
|
|
223
322
|
// Imported from inside netjes (netjes/vitals), which the dependency scan skips: found late, it would reload the page.
|
|
224
323
|
...(canResolve(root, 'web-vitals') ? ['web-vitals'] : []),
|
|
324
|
+
// A page package is not pre-bundled (below), so its CommonJS dependencies are, through it, as Vite asks.
|
|
325
|
+
...packages.flatMap((pkg) => commonJsDependencies(pkg).map((dependency) => `${pkg.name} > ${dependency}`)),
|
|
225
326
|
],
|
|
226
327
|
// netjes imports the virtual modules below, which pre-bundling cannot resolve, and the plugin loads its query
|
|
227
328
|
// shell by path: a pre-bundled copy of netjes would hold a second router, whose contexts the app's pages miss.
|
|
228
|
-
|
|
329
|
+
// A page package's pages are loaded by path, so a pre-bundled copy of it would be a second one of each of its
|
|
330
|
+
// modules too.
|
|
331
|
+
exclude: ['netjes', ...listed],
|
|
229
332
|
},
|
|
230
333
|
// netjes imports the plugin's virtual modules and must share its module instances with the app's pages, so Vite
|
|
231
|
-
// processes it in development as well
|
|
232
|
-
|
|
334
|
+
// processes it in development as well, and a page package's TypeScript sources with it; production bundles
|
|
335
|
+
// everything for the sidecar.
|
|
336
|
+
ssr: { noExternal: env.command === 'build' ? true : ['netjes', ...listed] },
|
|
233
337
|
build: isSsrBuild
|
|
234
338
|
? {
|
|
235
339
|
outDir: 'dist/server',
|
|
@@ -254,27 +358,52 @@ export default function netjes(options = {}) {
|
|
|
254
358
|
// takes only the convention files, so it would leave one out without a word: it stops on it too, saying the same.
|
|
255
359
|
async buildStart() {
|
|
256
360
|
if (!isBuild) return;
|
|
257
|
-
|
|
361
|
+
let handler = await findRouteHandler(rootDir, appDir);
|
|
362
|
+
// A package's, by its file id, as the route table names it.
|
|
363
|
+
for (const pkg of packages) {
|
|
364
|
+
if (handler) break;
|
|
365
|
+
const found = await findRouteHandler(pkg.root, pkg.pages);
|
|
366
|
+
if (found) handler = `${pkg.name}/${found}`;
|
|
367
|
+
}
|
|
258
368
|
if (handler) {
|
|
259
369
|
this.error(`'${handler}': route handlers are C# in Netjes. Map a minimal API endpoint instead (app.MapGet, app.MapPost, ...).`);
|
|
260
370
|
}
|
|
261
371
|
},
|
|
262
372
|
configureServer(server) {
|
|
263
373
|
devServer = server;
|
|
374
|
+
// Vite watches the root only, so a linked package's files, which can change while the app runs, are added. The
|
|
375
|
+
// map imports a package's files one by one, so a page that comes or goes builds it again, as the glob would.
|
|
376
|
+
const linked = packages.filter((pkg) => pkg.linked);
|
|
377
|
+
if (linked.length === 0) return;
|
|
378
|
+
server.watcher.add([...new Set(linked.flatMap((pkg) => [pkg.pages, ...pkg.sources].map((dir) => `${pkg.root}/${dir}`)))]);
|
|
379
|
+
/** @param {string} file */
|
|
380
|
+
const pageAddedOrRemoved = (file) => {
|
|
381
|
+
const name = normalizePath(file);
|
|
382
|
+
if (isConventionFile(path.posix.basename(name)) && linked.some((pkg) => name.startsWith(`${pkg.root}/${pkg.pages}/`))) rescanPages();
|
|
383
|
+
};
|
|
384
|
+
server.watcher.on('add', pageAddedOrRemoved);
|
|
385
|
+
server.watcher.on('unlink', pageAddedOrRemoved);
|
|
264
386
|
},
|
|
265
387
|
// For the development sidecar, which finds this plugin in its dev server's config.
|
|
266
388
|
api: {
|
|
267
389
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
390
|
+
* The URL the dev server loads a file by, from the file's id in a payload: `/app/...` for the app's, and for a
|
|
391
|
+
* listed package's the URL of its real path (`/node_modules/...` inside the root, `/@fs/...` outside it). Null for
|
|
392
|
+
* a file of an installed package that netjes({ packages }) does not list, which the page module map lacks.
|
|
393
|
+
* @param {string} id
|
|
394
|
+
* @returns {string | null}
|
|
270
395
|
*/
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
396
|
+
pageUrl(id) {
|
|
397
|
+
const owned = packageFile(id);
|
|
398
|
+
if (!owned) return unlistedPackage(id) ? null : `/${id}`;
|
|
399
|
+
const file = filePath(owned.pkg, owned.file);
|
|
400
|
+
const relative = path.posix.relative(normalizePath(rootDir), file);
|
|
401
|
+
return relative.startsWith('../') || path.isAbsolute(relative) ? `/@fs/${file.replace(/^\//, '')}` : `/${relative}`;
|
|
277
402
|
},
|
|
403
|
+
unlistedPackage,
|
|
404
|
+
/** The page packages the app lists. */
|
|
405
|
+
packages: () => packages,
|
|
406
|
+
rescanPages,
|
|
278
407
|
},
|
|
279
408
|
resolveId(id) {
|
|
280
409
|
if (id === PAGES_ID) return RESOLVED_PAGES_ID;
|
|
@@ -282,22 +411,71 @@ export default function netjes(options = {}) {
|
|
|
282
411
|
if (id === DEV_CLIENT_ENTRY_URL) return CLIENT_ENTRY;
|
|
283
412
|
return undefined;
|
|
284
413
|
},
|
|
285
|
-
load(id) {
|
|
414
|
+
async load(id) {
|
|
286
415
|
if (id === RESOLVED_QUERY_ID) {
|
|
287
416
|
if (!query) return 'export const QueryShell = undefined;\n';
|
|
288
417
|
return `import { createQueryShell } from ${JSON.stringify(QUERY_MODULE)};\nexport const QueryShell = createQueryShell(${queryLiteral});\n`;
|
|
289
418
|
}
|
|
290
419
|
if (id !== RESOLVED_PAGES_ID) return undefined;
|
|
291
|
-
const glob = JSON.stringify([appFiles])
|
|
420
|
+
const glob = `import.meta.glob(${JSON.stringify([appFiles])})`;
|
|
421
|
+
// A package's files are imported one by one, by their real paths: a glob leaves out node_modules, and would name a
|
|
422
|
+
// linked package's files by their paths through the link. Listed again each time, as the glob is.
|
|
423
|
+
const imported = (
|
|
424
|
+
await Promise.all(
|
|
425
|
+
packages.map(async (pkg) =>
|
|
426
|
+
(await pagePackageFiles(pkg)).map((file) => `${JSON.stringify(`/${fileId(pkg, file)}`)}: () => import(${JSON.stringify(filePath(pkg, file))})`),
|
|
427
|
+
),
|
|
428
|
+
)
|
|
429
|
+
).flat();
|
|
430
|
+
const pages = packages.length === 0 ? glob : `{ ...${glob}, ${imported.join(', ')} }`;
|
|
292
431
|
// The folder the keys start with: renderDocument (netjes/testing) builds routes and segment ids from it.
|
|
293
|
-
return
|
|
432
|
+
return (
|
|
433
|
+
`export const pages = ${pages};\nexport const appDir = ${JSON.stringify(appDir)};\n` +
|
|
434
|
+
`export const packages = ${JSON.stringify(packages.map(({ name, pages }) => ({ name, pages })))};\n`
|
|
435
|
+
);
|
|
294
436
|
},
|
|
295
437
|
async writeBundle() {
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
438
|
+
if (isSsrBuild) {
|
|
439
|
+
await mkdir(outDir, { recursive: true });
|
|
440
|
+
await copyFile(SIDECAR, path.join(outDir, 'netjes-sidecar.mjs'));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (packages.length === 0) return;
|
|
444
|
+
// Where the .NET app finds the packages in production, next to the manifest: no node_modules there to read them
|
|
445
|
+
// from, and a manifest key alone does not say which package a file is of.
|
|
446
|
+
const record = {
|
|
447
|
+
packages: await Promise.all(
|
|
448
|
+
packages.map(async (pkg) => ({
|
|
449
|
+
name: pkg.name,
|
|
450
|
+
version: pkg.version,
|
|
451
|
+
netjes: pkg.netjes,
|
|
452
|
+
pages: pkg.pages,
|
|
453
|
+
// Each file's id, and its key in the manifest: its path from the root, as Vite names a chunk's source.
|
|
454
|
+
files: Object.fromEntries((await pagePackageFiles(pkg)).map((file) => [fileId(pkg, file), normalizePath(path.relative(rootDir, filePath(pkg, file)))])),
|
|
455
|
+
// The NuGet package of its C# half, which the app names when the two halves are different versions.
|
|
456
|
+
...(pkg.nuget === null ? {} : { nuget: pkg.nuget }),
|
|
457
|
+
})),
|
|
458
|
+
),
|
|
459
|
+
};
|
|
460
|
+
await mkdir(path.join(outDir, '.vite'), { recursive: true });
|
|
461
|
+
await writeFile(path.join(outDir, '.vite', 'netjes.json'), `${JSON.stringify(record, null, 2)}\n`);
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Adds each page package's stylesheet and source folders to the app's Tailwind CSS build. Before @tailwindcss/vite,
|
|
467
|
+
* whose plugins run before the others too (`enforce: 'pre'`), so the order in the list decides.
|
|
468
|
+
* @type {import('vite').Plugin}
|
|
469
|
+
*/
|
|
470
|
+
const packageStyles = {
|
|
471
|
+
name: 'netjes:package-styles',
|
|
472
|
+
enforce: 'pre',
|
|
473
|
+
transform(code, id) {
|
|
474
|
+
if (!isTailwindStylesheet(id)) return undefined;
|
|
475
|
+
const css = withPackageStyles(code, id.split('?')[0] ?? id, packages);
|
|
476
|
+
return css === code ? undefined : { code: css, map: null };
|
|
299
477
|
},
|
|
300
478
|
};
|
|
301
479
|
|
|
302
|
-
return [react(options.react), plugin, options.tailwind === false ? null : tailwind()];
|
|
480
|
+
return [react(options.react), plugin, packageNames.length > 0 ? packageStyles : null, options.tailwind === false ? null : tailwind()];
|
|
303
481
|
}
|