@monkeyplus/flow 4.0.0-beta.8 → 5.0.0-beta.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.
Files changed (90) hide show
  1. package/app.d.ts +1 -0
  2. package/bin/flow.mjs +2 -0
  3. package/build.config.ts +25 -0
  4. package/dist/app/composables/index.d.ts +4 -0
  5. package/dist/app/composables/index.mjs +12 -0
  6. package/dist/app/entry.d.ts +3 -0
  7. package/dist/app/entry.mjs +23 -0
  8. package/dist/app/flow.d.ts +73 -0
  9. package/dist/app/flow.mjs +85 -0
  10. package/dist/app/index.d.ts +3 -0
  11. package/dist/app/index.mjs +3 -0
  12. package/dist/core/runtime/nitro/flow.d.ts +3 -0
  13. package/dist/core/runtime/nitro/flow.mjs +32 -0
  14. package/dist/core/runtime/nitro/paths.d.ts +4 -0
  15. package/dist/core/runtime/nitro/paths.mjs +15 -0
  16. package/dist/core/runtime/nitro/renderer.d.ts +2 -0
  17. package/dist/core/runtime/nitro/renderer.mjs +59 -0
  18. package/dist/head/runtime/composables.d.ts +9 -0
  19. package/dist/head/runtime/composables.mjs +2 -0
  20. package/dist/head/runtime/index.d.ts +1 -0
  21. package/dist/head/runtime/index.mjs +1 -0
  22. package/dist/head/runtime/plugin.d.ts +2 -0
  23. package/dist/head/runtime/plugin.mjs +6 -0
  24. package/dist/index.d.ts +8 -61
  25. package/dist/index.mjs +1275 -954
  26. package/dist/pages/runtime/helpers/chunks.d.ts +0 -0
  27. package/dist/pages/runtime/helpers/chunks.mjs +0 -0
  28. package/dist/pages/runtime/helpers/index.d.ts +5 -0
  29. package/dist/pages/runtime/helpers/index.mjs +28 -0
  30. package/dist/pages/runtime/plugin.d.ts +2 -0
  31. package/dist/pages/runtime/plugin.mjs +51 -0
  32. package/dist/vite-client/runtime/injectManifest.d.ts +26 -0
  33. package/dist/vite-client/runtime/injectManifest.mjs +104 -0
  34. package/dist/vite-client/runtime/plugin.d.ts +2 -0
  35. package/dist/vite-client/runtime/plugin.mjs +27 -0
  36. package/package.json +55 -36
  37. package/src/app/composables/index.ts +20 -0
  38. package/src/app/entry.ts +36 -0
  39. package/src/app/flow.ts +157 -0
  40. package/src/app/index.ts +5 -0
  41. package/src/auto-imports/module.ts +143 -0
  42. package/src/auto-imports/presets.ts +49 -0
  43. package/src/auto-imports/transform.ts +48 -0
  44. package/src/core/app.ts +90 -0
  45. package/src/core/builder.ts +59 -0
  46. package/src/core/flow.ts +93 -0
  47. package/src/core/modules.ts +32 -0
  48. package/src/core/nitro.ts +206 -0
  49. package/src/core/plugins/import-protection.ts +49 -0
  50. package/src/core/plugins/unctx.ts +31 -0
  51. package/src/core/runtime/nitro/flow.ts +43 -0
  52. package/src/core/runtime/nitro/paths.ts +20 -0
  53. package/src/core/runtime/nitro/renderer.ts +72 -0
  54. package/src/core/templates.ts +119 -0
  55. package/src/core/vite/builder/css.ts +28 -0
  56. package/src/core/vite/builder/dev-bundler.ts +247 -0
  57. package/src/core/vite/builder/index.ts +92 -0
  58. package/src/core/vite/builder/manifest.ts +33 -0
  59. package/src/core/vite/builder/plugins/analyze.ts +32 -0
  60. package/src/core/vite/builder/plugins/cache-dir.ts +13 -0
  61. package/src/core/vite/builder/plugins/dynamic-base.ts +64 -0
  62. package/src/core/vite/builder/plugins/virtual.ts +45 -0
  63. package/src/core/vite/builder/server.ts +163 -0
  64. package/src/core/vite/builder/types/index.ts +13 -0
  65. package/src/core/vite/builder/utils/index.ts +53 -0
  66. package/src/core/vite/builder/utils/warmup.ts +27 -0
  67. package/src/core/vite/builder/utils/wpfs.ts +7 -0
  68. package/src/core/vite/builder/vite-node.ts +110 -0
  69. package/src/core/vite/client/index.ts +48 -0
  70. package/src/dirs.ts +8 -0
  71. package/src/head/module.ts +37 -0
  72. package/src/head/runtime/composables.ts +16 -0
  73. package/src/head/runtime/index.ts +1 -0
  74. package/src/head/runtime/plugin.ts +12 -0
  75. package/src/index.ts +2 -0
  76. package/src/pages/module.ts +55 -0
  77. package/src/pages/runtime/helpers/chunks.ts +0 -0
  78. package/src/pages/runtime/helpers/index.ts +33 -0
  79. package/src/pages/runtime/plugin.ts +58 -0
  80. package/src/pages/templates.ts +20 -0
  81. package/src/pages/utils.ts +49 -0
  82. package/src/vite-client/module.ts +70 -0
  83. package/src/vite-client/runtime/injectManifest.ts +188 -0
  84. package/src/vite-client/runtime/plugin.ts +33 -0
  85. package/types.d.ts +2 -0
  86. package/dist/index.cjs +0 -1061
  87. package/types/core.d.ts +0 -143
  88. package/types/flow.d.ts +0 -239
  89. package/types/index.d.ts +0 -38
  90. package/types/interfaces.d.ts +0 -61
@@ -0,0 +1,247 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ import { existsSync } from 'node:fs';
3
+ import { builtinModules } from 'node:module';
4
+ import { resolve } from 'pathe';
5
+ import type * as vite from 'vite';
6
+ import type { ExternalsOptions } from 'externality';
7
+ import { ExternalsDefaults, isExternal as _isExternal } from 'externality';
8
+ import { genDynamicImport, genObjectFromRawEntries } from 'knitwork';
9
+ import { hashId, uniq } from './utils';
10
+
11
+ export interface TransformChunk {
12
+ id: string
13
+ code: string
14
+ deps: string[]
15
+ parents: string[]
16
+ }
17
+
18
+ export interface SSRTransformResult {
19
+ code: string
20
+ map: object
21
+ deps: string[]
22
+ dynamicDeps: string[]
23
+ }
24
+
25
+ export interface TransformOptions {
26
+ viteServer: vite.ViteDevServer
27
+ }
28
+
29
+ function isExternal(opts: TransformOptions, id: string) {
30
+ // Externals
31
+ const ssrConfig = (opts.viteServer.config as any).ssr;
32
+
33
+ const externalOpts: ExternalsOptions = {
34
+ inline: [
35
+ /virtual:/,
36
+ /\.ts$/,
37
+ ...ExternalsDefaults.inline,
38
+ ...ssrConfig.noExternal,
39
+ ],
40
+ external: [
41
+ ...ssrConfig.external,
42
+ /node_modules/,
43
+ ],
44
+ resolve: {
45
+ type: 'module',
46
+ extensions: ['.ts', '.js', '.json', '.vue', '.mjs', '.jsx', '.tsx', '.wasm'],
47
+ },
48
+ };
49
+ return _isExternal(id, opts.viteServer.config.root, externalOpts);
50
+ }
51
+
52
+ async function transformRequest(opts: TransformOptions, id: string) {
53
+ // Virtual modules start with `\0`
54
+ if (id && id.startsWith('/@id/__x00__'))
55
+ id = `\0${id.slice('/@id/__x00__'.length)}`;
56
+
57
+ if (id && id.startsWith('/@id/'))
58
+ id = id.slice('/@id/'.length);
59
+
60
+ if (id && id.startsWith('/@fs/')) {
61
+ // Absolute path
62
+ id = id.slice('/@fs'.length);
63
+ // On Windows, this may be `/C:/my/path` at this point, in which case we want to remove the `/`
64
+ if (id.match(/^\/\w:/))
65
+ id = id.slice(1);
66
+ }
67
+ else if (!id.includes('entry') && id.startsWith('/')) {
68
+ // Relative to the root directory
69
+ const resolvedPath = resolve(opts.viteServer.config.root, `.${id}`);
70
+ if (existsSync(resolvedPath))
71
+ id = resolvedPath;
72
+ }
73
+
74
+ // Vite will add ?v=123 to bypass browser cache
75
+ // Remove for externals
76
+ const withoutVersionQuery = id.replace(/\?v=\w+$/, '');
77
+ if (await isExternal(opts, withoutVersionQuery)) {
78
+ const path = builtinModules.includes(withoutVersionQuery.split('node:').pop())
79
+ ? withoutVersionQuery
80
+ : pathToFileURL(withoutVersionQuery).href;
81
+ return {
82
+ code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) =>
83
+ ${genDynamicImport(path, { wrapper: false })}
84
+ .then(r => {
85
+ if (r.default && r.default.__esModule)
86
+ r = r.default
87
+ exports.default = r.default
88
+ ssrExportAll(r)
89
+ })
90
+ .catch(e => {
91
+ console.error(e)
92
+ throw new Error(${JSON.stringify(`[vite dev] Error loading external "${id}".`)})
93
+ })`,
94
+ deps: [],
95
+ dynamicDeps: [],
96
+ };
97
+ }
98
+
99
+ // Transform
100
+ const res: SSRTransformResult = await opts.viteServer.transformRequest(id, { ssr: true }).catch((err) => {
101
+ // eslint-disable-next-line no-console
102
+ console.warn(`[SSR] Error transforming ${id}:`, err);
103
+ // console.error(err)
104
+ }) as SSRTransformResult || { code: '', map: {}, deps: [], dynamicDeps: [] };
105
+
106
+ // Wrap into a vite module
107
+ const code = `async function (global, module, exports, __vite_ssr_exports__, __vite_ssr_import_meta__, __vite_ssr_import__, __vite_ssr_dynamic_import__, __vite_ssr_exportAll__) {
108
+ ${res.code || '/* empty */'};
109
+ }`;
110
+ return { code, deps: res.deps || [], dynamicDeps: res.dynamicDeps || [] };
111
+ }
112
+
113
+ async function transformRequestRecursive(opts: TransformOptions, id: any, parent = '<entry>', chunks: Record<string, TransformChunk> = {}) {
114
+ if (chunks[id]) {
115
+ chunks[id].parents.push(parent);
116
+ return;
117
+ }
118
+ const res = await transformRequest(opts, id);
119
+ const deps = uniq([...res.deps, ...res.dynamicDeps]);
120
+
121
+ chunks[id] = {
122
+ id,
123
+ code: res.code,
124
+ deps,
125
+ parents: [parent],
126
+ } as TransformChunk;
127
+ for (const dep of deps)
128
+ await transformRequestRecursive(opts, dep, id, chunks);
129
+
130
+ return Object.values(chunks);
131
+ }
132
+
133
+ export async function bundleRequest(opts: TransformOptions, entryURL: string) {
134
+ const chunks = await transformRequestRecursive(opts, entryURL);
135
+
136
+ const listIds = (ids: string[]) => ids.map((id) => `// - ${id} (${hashId(id)})`).join('\n');
137
+ const chunksCode = chunks!.map((chunk) => `
138
+ // --------------------
139
+ // Request: ${chunk.id}
140
+ // Parents: \n${listIds(chunk.parents)}
141
+ // Dependencies: \n${listIds(chunk.deps)}
142
+ // --------------------
143
+ const ${hashId(chunk.id)} = ${chunk.code}
144
+ `).join('\n');
145
+
146
+ const manifestCode = `const __modules__ = ${
147
+ genObjectFromRawEntries(chunks!.map((chunk) => [chunk.id, hashId(chunk.id)]))
148
+ }`;
149
+
150
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrModuleLoader.ts
151
+ const ssrModuleLoader = `
152
+ const __pendingModules__ = new Map()
153
+ const __pendingImports__ = new Map()
154
+ const __ssrContext__ = { global: globalThis }
155
+
156
+ function __ssrLoadModule__(url, urlStack = []) {
157
+ const pendingModule = __pendingModules__.get(url)
158
+ if (pendingModule) { return pendingModule }
159
+ const modulePromise = __instantiateModule__(url, urlStack)
160
+ __pendingModules__.set(url, modulePromise)
161
+ modulePromise.catch(() => { __pendingModules__.delete(url) })
162
+ .finally(() => { __pendingModules__.delete(url) })
163
+ return modulePromise
164
+ }
165
+
166
+ async function __instantiateModule__(url, urlStack) {
167
+ const mod = __modules__[url]
168
+ if (mod.stubModule) { return mod.stubModule }
169
+ const stubModule = { [Symbol.toStringTag]: 'Module' }
170
+ Object.defineProperty(stubModule, '__esModule', { value: true })
171
+ mod.stubModule = stubModule
172
+ // https://vitejs.dev/guide/api-hmr.html
173
+ const importMeta = { url, hot: { accept() {}, prune() {}, dispose() {}, invalidate() {}, decline() {}, on() {} } }
174
+ urlStack = urlStack.concat(url)
175
+ const isCircular = url => urlStack.includes(url)
176
+ const pendingDeps = []
177
+ const ssrImport = async (dep) => {
178
+ // TODO: Handle externals if dep[0] !== '.' | '/'
179
+ if (!isCircular(dep) && !__pendingImports__.get(dep)?.some(isCircular)) {
180
+ pendingDeps.push(dep)
181
+ if (pendingDeps.length === 1) {
182
+ __pendingImports__.set(url, pendingDeps)
183
+ }
184
+ await __ssrLoadModule__(dep, urlStack)
185
+ if (pendingDeps.length === 1) {
186
+ __pendingImports__.delete(url)
187
+ } else {
188
+ pendingDeps.splice(pendingDeps.indexOf(dep), 1)
189
+ }
190
+ }
191
+ return __modules__[dep].stubModule
192
+ }
193
+ function ssrDynamicImport (dep) {
194
+ // TODO: Handle dynamic import starting with . relative to url
195
+ return ssrImport(dep)
196
+ }
197
+
198
+ function ssrExportAll(sourceModule) {
199
+ for (const key in sourceModule) {
200
+ if (key !== 'default') {
201
+ try {
202
+ Object.defineProperty(stubModule, key, {
203
+ enumerable: true,
204
+ configurable: true,
205
+ get() { return sourceModule[key] }
206
+ })
207
+ } catch (_err) { }
208
+ }
209
+ }
210
+ }
211
+
212
+ const cjsModule = {
213
+ get exports () {
214
+ return stubModule.default
215
+ },
216
+ set exports (v) {
217
+ stubModule.default = v
218
+ },
219
+ }
220
+
221
+ await mod(
222
+ __ssrContext__.global,
223
+ cjsModule,
224
+ stubModule.default,
225
+ stubModule,
226
+ importMeta,
227
+ ssrImport,
228
+ ssrDynamicImport,
229
+ ssrExportAll
230
+ )
231
+
232
+ return stubModule
233
+ }
234
+ `;
235
+
236
+ const code = [
237
+ chunksCode,
238
+ manifestCode,
239
+ ssrModuleLoader,
240
+ `export default await __ssrLoadModule__(${JSON.stringify(entryURL)})`,
241
+ ].join('\n\n');
242
+
243
+ return {
244
+ code,
245
+ ids: chunks!.map((i) => i.id),
246
+ };
247
+ }
@@ -0,0 +1,92 @@
1
+ import { isIgnoredFlow, logger } from '@monkeyplus/flow-kit';
2
+ import type { Flow } from '@monkeyplus/flow-schema';
3
+ import { getPort } from 'get-port-please';
4
+ import * as vite from 'vite';
5
+ import { resolve } from 'pathe';
6
+ import { sanitizeFilePath } from 'mlly';
7
+ import replace from '@rollup/plugin-replace';
8
+ import { warmupViteServer } from './utils/warmup';
9
+ import { buildServer } from './server';
10
+ import type { ViteBuildContext } from './types';
11
+ import virtual from './plugins/virtual';
12
+ import { DynamicBasePlugin } from './plugins/dynamic-base';
13
+
14
+ export async function bundleVite(flow: Flow) {
15
+ const hmrPortDefault = 24678; // Vite's default HMR port
16
+ const hmrPort = await getPort({
17
+ port: hmrPortDefault,
18
+ ports: Array.from({ length: 20 }, (_, i) => hmrPortDefault + 1 + i),
19
+ });
20
+
21
+ const ctx: ViteBuildContext = {
22
+ nuxt: flow,
23
+ flow,
24
+ config: vite.mergeConfig({
25
+ resolve: {
26
+ alias: {
27
+ ...flow.options.alias,
28
+ '#app': flow.options.appDir,
29
+ '#build/plugins': resolve(flow.options.buildDir, 'plugins/server'),
30
+ '#build': flow.options.buildDir,
31
+ '/entry.mjs': resolve(flow.options.appDir, 'entry'),
32
+
33
+ },
34
+ },
35
+ optimizeDeps: {
36
+ entries: [
37
+ resolve(flow.options.appDir, 'entry.ts'),
38
+ ],
39
+ include: [],
40
+ },
41
+ // css: resolveCSSOptions(nuxt),
42
+ build: {
43
+ rollupOptions: {
44
+ output: { sanitizeFileName: sanitizeFilePath },
45
+ input: resolve(flow.options.appDir, 'entry'),
46
+ },
47
+ },
48
+ plugins: [
49
+ replace({
50
+ ...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map((d) => [`${d}global.`, `${d}globalThis.`])),
51
+ preventAssignment: true,
52
+ }),
53
+ virtual(flow.vfs),
54
+ DynamicBasePlugin.vite({ sourcemap: flow.options.sourcemap }),
55
+ ],
56
+ server: {
57
+ watch: {
58
+ ignored: isIgnoredFlow,
59
+ },
60
+ hmr: {
61
+ // https://github.com/nuxt/framework/issues/4191
62
+ protocol: 'ws',
63
+ clientPort: hmrPort,
64
+ port: hmrPort,
65
+ },
66
+ fs: {
67
+ allow: [
68
+ flow.options.appDir,
69
+ ],
70
+ },
71
+ },
72
+ }, flow.options.vite),
73
+ };
74
+ await flow.callHook('vite:extend', ctx);
75
+
76
+ flow.hook('vite:serverCreated', (server: vite.ViteDevServer) => {
77
+ // Invalidate virtual modules when templates are re-generated
78
+ ctx.nuxt.hook('app:templatesGenerated', () => {
79
+ for (const [id, mod] of server.moduleGraph.idToModuleMap) {
80
+ if (id.startsWith('\x00virtual:'))
81
+ server.moduleGraph.invalidateModule(mod);
82
+ }
83
+ });
84
+
85
+ const start = Date.now();
86
+ warmupViteServer(server, ['/entry.mjs'])
87
+ .then(() => logger.info(`Vite server warmed up in ${Date.now() - start}ms`))
88
+ .catch(logger.error);
89
+ });
90
+
91
+ await buildServer(ctx);
92
+ }
@@ -0,0 +1,33 @@
1
+ import fse from 'fs-extra';
2
+ import { resolve } from 'pathe';
3
+ import { joinURL } from 'ufo';
4
+ import type { ViteBuildContext } from './types';
5
+
6
+ export async function writeManifest(ctx: ViteBuildContext, extraEntries: string[] = []) {
7
+ // Write client manifest for use in vue-bundle-renderer
8
+ const clientDist = resolve(ctx.flow.options.buildDir, 'dist/client');
9
+ const serverDist = resolve(ctx.flow.options.buildDir, 'dist/server');
10
+
11
+ const entries = [
12
+ '@vite/client',
13
+ 'entry.mjs',
14
+ ...extraEntries,
15
+ ];
16
+
17
+ // Legacy dev manifest
18
+ const devClientManifest = {
19
+ publicPath: joinURL(ctx.flow.options.app.baseURL, ctx.flow.options.app.buildAssetsDir),
20
+ all: entries,
21
+ initial: entries,
22
+ async: [],
23
+ modules: {},
24
+ };
25
+
26
+ const clientManifest = ctx.flow.options.dev
27
+ ? devClientManifest
28
+ : await fse.readJSON(resolve(clientDist, 'manifest.json'));
29
+
30
+ await fse.mkdirp(serverDist);
31
+ await fse.writeFile(resolve(serverDist, 'client.manifest.json'), JSON.stringify(clientManifest, null, 2), 'utf8');
32
+ await fse.writeFile(resolve(serverDist, 'client.manifest.mjs'), `export default ${JSON.stringify(clientManifest, null, 2)}`, 'utf8');
33
+ }
@@ -0,0 +1,32 @@
1
+ // import type { Plugin } from 'vite';
2
+ // import { transform } from 'esbuild';
3
+ // // import { visualizer } from 'rollup-plugin-visualizer';
4
+ // import type { ViteBuildContext } from '../types';
5
+
6
+ // export function analyzePlugin(ctx: ViteBuildContext): Plugin[] {
7
+ // return [
8
+ // {
9
+ // name: 'nuxt:analyze-minify',
10
+ // async generateBundle(_opts, outputBundle) {
11
+ // for (const [_bundleId, bundle] of Object.entries(outputBundle)) {
12
+ // if (bundle.type !== 'chunk') continue;
13
+ // const originalEntries = Object.entries(bundle.modules);
14
+ // const minifiedEntries = await Promise.all(originalEntries.map(async([moduleId, module]) => {
15
+ // const { code } = await transform(module.code || '', { minify: true });
16
+ // return [moduleId, { ...module, code }];
17
+ // }));
18
+ // bundle.modules = Object.fromEntries(minifiedEntries);
19
+ // }
20
+ // return null;
21
+ // },
22
+ // },
23
+ // // visualizer({
24
+ // // // @ts-ignore
25
+ // // ...ctx.flow.options?.build?.analyze as any,
26
+ // // // @ts-ignore
27
+ // // filename: ctx.flow.options.build.analyze.filename.replace('{name}', 'client'),
28
+ // // title: 'Client bundle stats',
29
+ // // gzipSize: true,
30
+ // // }),
31
+ // ];
32
+ // }
@@ -0,0 +1,13 @@
1
+ import { resolve } from 'pathe';
2
+ import type { Plugin } from 'vite';
3
+
4
+ export function cacheDirPlugin(rootDir: string, name: string) {
5
+ const optimizeCacheDir = resolve(rootDir, 'node_modules/.cache/vite', name);
6
+ return <Plugin> {
7
+ name: 'flow:cache-dir',
8
+ configResolved(resolvedConfig) {
9
+ // @ts-ignore
10
+ resolvedConfig.optimizeCacheDir = optimizeCacheDir;
11
+ },
12
+ };
13
+ }
@@ -0,0 +1,64 @@
1
+ import { createUnplugin } from 'unplugin';
2
+
3
+ import MagicString from 'magic-string';
4
+
5
+ interface DynamicBasePluginOptions {
6
+ globalPublicPath?: string
7
+ sourcemap?: boolean
8
+ }
9
+
10
+ const VITE_ASSET_RE = /^export default ["'](__VITE_ASSET.*)["']$/;
11
+
12
+ export const DynamicBasePlugin = createUnplugin((options: DynamicBasePluginOptions = {}) => {
13
+ return {
14
+ name: 'nuxt:dynamic-base-path',
15
+ resolveId(id) {
16
+ if (id.startsWith('/__NUXT_BASE__'))
17
+ return id.replace('/__NUXT_BASE__', '');
18
+
19
+ if (id === '#internal/nitro') return '#internal/nitro';
20
+ return null;
21
+ },
22
+ enforce: 'post',
23
+ transform(code, id) {
24
+ const s = new MagicString(code);
25
+
26
+ if (options.globalPublicPath && id.includes('paths.mjs') && code.includes('const appConfig = '))
27
+ s.append(`${options.globalPublicPath} = buildAssetsURL();\n`);
28
+
29
+ const assetId = code.match(VITE_ASSET_RE);
30
+ if (assetId) {
31
+ s.overwrite(0, code.length,
32
+ [
33
+ 'import { buildAssetsURL } from \'#build/paths.mjs\';',
34
+ `export default buildAssetsURL("${assetId[1]}".replace("/__NUXT_BASE__", ""));`,
35
+ ].join('\n'),
36
+ );
37
+ }
38
+
39
+ if (!id.includes('paths.mjs') && code.includes('NUXT_BASE') && !code.includes('import { publicAssetsURL as __publicAssetsURL }'))
40
+ s.prepend('import { publicAssetsURL as __publicAssetsURL } from \'#build/paths.mjs\';\n');
41
+
42
+ if (id === 'vite/preload-helper') {
43
+ // Define vite base path as buildAssetsUrl (i.e. including _nuxt/)
44
+ s.prepend('import { buildAssetsDir } from \'#build/paths.mjs\';\n');
45
+ s.replace(/const base = ['"]\/__NUXT_BASE__\/['"]/, 'const base = buildAssetsDir()');
46
+ }
47
+
48
+ // Sanitize imports
49
+ s.replace(/from *['"]\/__NUXT_BASE__(\/[^'"]*)['"]/g, 'from "$1"');
50
+
51
+ // Dynamically compute string URLs featuring baseURL
52
+ const delimiterRE = /(?<!(const base = |from *))(`([^`]*)\/__NUXT_BASE__\/([^`]*)`|'([^']*)\/__NUXT_BASE__\/([^']*)'|"([^"]*)\/__NUXT_BASE__\/([^"]*)")/g;
53
+ /* eslint-disable-next-line no-template-curly-in-string */
54
+ s.replace(delimiterRE, (r) => `\`${r.replace(/\/__NUXT_BASE__\//g, '${__publicAssetsURL()}').slice(1, -1)}\``);
55
+
56
+ if (s.hasChanged()) {
57
+ return {
58
+ code: s.toString(),
59
+ map: options.sourcemap && s.generateMap({ source: id, includeContent: true }),
60
+ };
61
+ }
62
+ },
63
+ };
64
+ });
@@ -0,0 +1,45 @@
1
+ import { dirname, isAbsolute, join, resolve } from 'pathe';
2
+ import type { Plugin } from 'rollup';
3
+
4
+ const PREFIX = 'virtual:';
5
+
6
+ export default function virtual(vfs: Record<string, string>): Plugin {
7
+ const extensions = ['', '.ts', '.vue', '.mjs', '.cjs', '.js', '.json'];
8
+ const resolveWithExt = (id: any) => {
9
+ for (const ext of extensions) {
10
+ const rId = id + ext;
11
+ if (rId in vfs)
12
+ return rId;
13
+ }
14
+ return null;
15
+ };
16
+
17
+ return {
18
+ name: 'virtual',
19
+
20
+ resolveId(id, importer) {
21
+ if (process.platform === 'win32' && isAbsolute(id)) {
22
+ // Add back C: prefix on Windows
23
+ id = resolve(id);
24
+ }
25
+ const resolvedId = resolveWithExt(id);
26
+ if (resolvedId) return PREFIX + resolvedId;
27
+ if (importer && !isAbsolute(id)) {
28
+ const importerNoPrefix = importer.startsWith(PREFIX) ? importer.slice(PREFIX.length) : importer;
29
+ const importedDir = dirname(importerNoPrefix);
30
+ const resolved = resolveWithExt(join(importedDir, id));
31
+ if (resolved) return PREFIX + resolved;
32
+ }
33
+ return null;
34
+ },
35
+
36
+ load(id) {
37
+ if (!id.startsWith(PREFIX)) return null;
38
+ const idNoPrefix = id.slice(PREFIX.length);
39
+ return {
40
+ code: vfs[idNoPrefix],
41
+ map: null,
42
+ };
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,163 @@
1
+ import { join, normalize, resolve } from 'pathe';
2
+ import * as vite from 'vite';
3
+ import fse from 'fs-extra';
4
+ import { withoutTrailingSlash } from 'ufo';
5
+ import { logger } from '@monkeyplus/flow-kit';
6
+ import { debounce } from 'perfect-debounce';
7
+ import { cacheDirPlugin } from './plugins/cache-dir';
8
+ import type { ViteBuildContext, ViteOptions } from './types';
9
+ import { wpfs } from './utils/wpfs';
10
+ import { bundleRequest } from './dev-bundler';
11
+ import { writeManifest } from './manifest';
12
+ import { isCSS, isDirectory, readDirRecursively } from './utils';
13
+
14
+ export const buildServer = async(ctx: ViteBuildContext) => {
15
+ // const _resolve = (id: string) => resolveModule(id, { paths: ctx.flow.options.modulesDir });
16
+
17
+ const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
18
+ configFile: false,
19
+ define: {
20
+ 'process.server': true,
21
+ 'typeof window': '"undefined"',
22
+ 'typeof document': '"undefined"',
23
+ 'typeof navigator': '"undefined"',
24
+ 'typeof location': '"undefined"',
25
+ 'typeof XMLHttpRequest': '"undefined"',
26
+ },
27
+ ssr: {
28
+ noExternal: [
29
+ ...ctx.flow.options.build.transpile,
30
+ // TODO: Use externality for production (rollup) build
31
+ /\/esm\/.*\.js$/,
32
+ /\.(es|esm|esm-browser|esm-bundler).js$/,
33
+ '/__vue-jsx',
34
+ '#app',
35
+ /(nuxt|nuxt3)\/(dist|src|app)/,
36
+ /@nuxt\/nitro\/(dist|src)/,
37
+ ],
38
+ },
39
+ build: {
40
+ outDir: resolve(ctx.flow.options.buildDir, 'dist/server'),
41
+ manifest: true,
42
+ rollupOptions: {
43
+ external: ['#internal/nitro'],
44
+ output: {
45
+ entryFileNames: 'server.mjs',
46
+ preferConst: true,
47
+ format: 'module',
48
+ },
49
+ onwarn(warning, rollupWarn) {
50
+ if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code))
51
+ rollupWarn(warning);
52
+ },
53
+ },
54
+ },
55
+ server: {
56
+ // https://github.com/vitest-dev/vitest/issues/229#issuecomment-1002685027
57
+ preTransformRequests: false,
58
+ cors: true,
59
+ },
60
+ plugins: [
61
+ cacheDirPlugin(ctx.flow.options.rootDir, 'server'),
62
+ ],
63
+ } as ViteOptions);
64
+
65
+ await ctx.flow.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true });
66
+
67
+ ctx.flow.hook('nitro:build:before', async() => {
68
+ if (ctx.flow.options.dev)
69
+ return;
70
+
71
+ const clientDist = resolve(ctx.flow.options.buildDir, 'dist/client');
72
+
73
+ // Remove public files that have been duplicated into buildAssetsDir
74
+ // TODO: Add option to configure this behavior in vite
75
+ const publicDir = join(ctx.flow.options.srcDir, ctx.flow.options.dir.public);
76
+ let publicFiles: string[] = [];
77
+ if (await isDirectory(publicDir)) {
78
+ publicFiles = readDirRecursively(publicDir).map((r) => r.replace(publicDir, ''));
79
+ for (const file of publicFiles) {
80
+ try {
81
+ fse.rmSync(join(clientDist, file));
82
+ }
83
+ catch {}
84
+ }
85
+ }
86
+
87
+ // Copy doubly-nested /_nuxt/_nuxt files into buildAssetsDir
88
+ // TODO: Workaround vite issue
89
+ if (await isDirectory(clientDist)) {
90
+ const nestedAssetsPath = withoutTrailingSlash(join(clientDist, ctx.flow.options.app.buildAssetsDir));
91
+
92
+ if (await isDirectory(nestedAssetsPath)) {
93
+ await fse.copy(nestedAssetsPath, clientDist, { recursive: true });
94
+ await fse.remove(nestedAssetsPath);
95
+ }
96
+ }
97
+ });
98
+
99
+ const onBuild = () => ctx.flow.callHook('build:resources', wpfs);
100
+
101
+ // Production build
102
+ if (!ctx.flow.options.dev) {
103
+ const start = Date.now();
104
+ logger.info('Building server...');
105
+ await vite.build(serverConfig);
106
+ await onBuild();
107
+ logger.success(`Server built in ${Date.now() - start}ms`);
108
+ return;
109
+ }
110
+
111
+ // if (!ctx.flow.options.ssr) {
112
+ // await onBuild();
113
+ // return;
114
+ // }
115
+ // console.log(serverConfig.plugins);
116
+
117
+ // Start development server
118
+ const viteServer = await vite.createServer(serverConfig);
119
+ ctx.ssrServer = viteServer;
120
+
121
+ await ctx.flow.callHook('vite:serverCreated', viteServer, { isClient: false, isServer: true });
122
+
123
+ // Close server on exit
124
+ ctx.flow.hook('close', () => viteServer.close());
125
+
126
+ // Initialize plugins
127
+ await viteServer.pluginContainer.buildStart({});
128
+ // console.log(viteServer.1);
129
+
130
+ // if (ctx.flow.options.experimental.viteNode) {
131
+ // logger.info('Vite server using experimental `vite-node`...');
132
+ // await prepareDevServerEntry(ctx);
133
+ // }
134
+ // else {
135
+ // Build and watch
136
+ const _doBuild = async() => {
137
+ const start = Date.now();
138
+ const { code, ids } = await bundleRequest({ viteServer }, resolve(ctx.flow.options.appDir, 'entry'));
139
+ await fse.ensureFile(resolve(ctx.flow.options.buildDir, 'dist/server/server.mjs'));
140
+ await fse.writeFile(resolve(ctx.flow.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8');
141
+ // Have CSS in the manifest to prevent FOUC on dev SSR
142
+ await writeManifest(ctx, ids.filter(isCSS).map((i) => i.slice(1)));
143
+ const time = (Date.now() - start);
144
+ logger.success(`Vite server built in ${time}ms`);
145
+ await onBuild();
146
+ ctx.flow.callHook('bundler:change', {} as any);
147
+ };
148
+ const doBuild = debounce(_doBuild);
149
+
150
+ // Initial build
151
+ await _doBuild();
152
+
153
+ // Watch
154
+ viteServer.watcher.on('all', (_event, file) => {
155
+ file = normalize(file); // Fix windows paths
156
+ if (file.indexOf(ctx.flow.options.buildDir) === 0) return;
157
+ doBuild();
158
+ });
159
+ ctx.flow.hook('app:templatesGenerated', () => doBuild());
160
+
161
+ // ctx.nuxt.hook('builder:watch', () => doBuild())
162
+ // }
163
+ };