zuby 1.0.42 → 1.0.44
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/commands/build.js +68 -42
- package/commands/dev.js +14 -5
- package/config.d.ts +20 -3
- package/config.js +73 -10
- package/defineConfig.d.ts +3 -1
- package/defineConfig.js +5 -0
- package/package.json +1 -1
- package/plugins/chunkNamingPlugin/index.d.ts +2 -2
- package/plugins/compileTimePlugin/index.d.ts +2 -2
- package/plugins/compileTimePlugin/index.js +65 -69
- package/plugins/contextPlugin/index.d.ts +2 -2
- package/plugins/dependenciesPlugin/index.d.ts +7 -0
- package/plugins/dependenciesPlugin/index.js +46 -0
- package/plugins/manifestPlugin/index.d.ts +2 -2
- package/plugins/prerenderPlugin/index.d.ts +2 -2
- package/plugins/prerenderPlugin/index.js +91 -101
- package/templates/index.js +1 -1
- package/types.d.ts +121 -7
- package/types.js +14 -0
package/commands/build.js
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
import { MODES } from '../types.js';
|
|
2
|
-
import { getZubyInternalConfig } from '../config.js';
|
|
1
|
+
import { MODES, PLUGIN_HOOKS } from '../types.js';
|
|
2
|
+
import { executePlugins, getZubyInternalConfig } from '../config.js';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { getTitle } from '../branding.js';
|
|
5
5
|
import { build as viteBuild } from 'vite';
|
|
6
|
-
import { dirname, join } from 'path';
|
|
6
|
+
import { dirname, join, resolve } from 'path';
|
|
7
7
|
import { normalizePath } from '../utils/pathUtils.js';
|
|
8
|
-
import { existsSync, rmSync, writeFileSync, copyFileSync,
|
|
8
|
+
import { existsSync, rmSync, writeFileSync, copyFileSync, readFileSync, } from 'fs';
|
|
9
9
|
import { TEMPLATES } from '../templates/types.js';
|
|
10
10
|
import { glob } from 'glob';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import { getZubyPackageConfig } from '../packageConfig.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { getTemplates } from '../templates/index.js';
|
|
14
|
+
import { CLIENT_CHUNKS_MANIFEST, SERVER_CHUNKS_MANIFEST } from '../constants.js';
|
|
15
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
16
|
const __dirname = dirname(__filename);
|
|
17
17
|
export default async function build(options) {
|
|
18
18
|
const zubyInternalConfig = await getZubyInternalConfig(options.configFile);
|
|
19
|
-
const { vite: viteConfig, customLogger: logger, outDir,
|
|
19
|
+
const { vite: viteConfig, customLogger: logger, outDir, cacheDir, plugins } = zubyInternalConfig;
|
|
20
|
+
let buildStep = 1;
|
|
21
|
+
const buildSteps = 2 + plugins.filter(plugin => plugin?.buildStep).length;
|
|
22
|
+
const nextBuildStep = (description) => {
|
|
23
|
+
logger?.info(`${chalk.bgYellow.bold.whiteBright(` Step ${buildStep++}/${buildSteps} `)} ${chalk.gray(description)}`);
|
|
24
|
+
};
|
|
20
25
|
process.env.NODE_ENV = MODES.production;
|
|
21
26
|
logger?.info(getTitle(chalk.gray(`building for production...`)));
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
rmSync(outDir || '', {
|
|
25
|
-
recursive: true,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
27
|
+
// Load templates from the project directory
|
|
28
|
+
const templates = await getTemplates();
|
|
28
29
|
// Load the entry file from the project directory
|
|
29
30
|
// or jsxProvider
|
|
30
31
|
const entryFile = await getEntryFile(zubyInternalConfig);
|
|
31
|
-
|
|
32
|
-
logger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 1/4 `)} ${chalk.gray(`building client...`)}`);
|
|
33
|
-
await viteBuild({
|
|
32
|
+
const clientViteBuildConfig = {
|
|
34
33
|
configFile: false,
|
|
35
34
|
...viteConfig,
|
|
36
35
|
build: {
|
|
@@ -45,10 +44,8 @@ export default async function build(options) {
|
|
|
45
44
|
},
|
|
46
45
|
cacheDir: normalizePath(join(cacheDir, 'client')),
|
|
47
46
|
mode: MODES.production,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
logger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 2/4 `)} ${chalk.gray(`building server...`)}`);
|
|
51
|
-
await viteBuild({
|
|
47
|
+
};
|
|
48
|
+
const serverViteBuildConfig = {
|
|
52
49
|
...viteConfig,
|
|
53
50
|
configFile: false,
|
|
54
51
|
publicDir: false,
|
|
@@ -60,6 +57,36 @@ export default async function build(options) {
|
|
|
60
57
|
},
|
|
61
58
|
cacheDir: normalizePath(join(cacheDir, 'server')),
|
|
62
59
|
mode: MODES.production,
|
|
60
|
+
};
|
|
61
|
+
// Run build setup hook
|
|
62
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyBuildSetup, {
|
|
63
|
+
clientViteBuildConfig,
|
|
64
|
+
serverViteBuildConfig,
|
|
65
|
+
templates,
|
|
66
|
+
});
|
|
67
|
+
// Run build start hook
|
|
68
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyBuildStart, {
|
|
69
|
+
templates,
|
|
70
|
+
});
|
|
71
|
+
// Clean build directory
|
|
72
|
+
if (existsSync(outDir || '')) {
|
|
73
|
+
rmSync(outDir || '', {
|
|
74
|
+
recursive: true,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Client build
|
|
78
|
+
nextBuildStep('building client...');
|
|
79
|
+
await viteBuild(clientViteBuildConfig);
|
|
80
|
+
// Run build client done hook
|
|
81
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyBuildClientDone, {
|
|
82
|
+
templates,
|
|
83
|
+
});
|
|
84
|
+
// Server build
|
|
85
|
+
nextBuildStep('building server...');
|
|
86
|
+
await viteBuild(serverViteBuildConfig);
|
|
87
|
+
// Run build server done hook
|
|
88
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyBuildServerDone, {
|
|
89
|
+
templates,
|
|
63
90
|
});
|
|
64
91
|
// Add serialized zuby config to build directory
|
|
65
92
|
writeFileSync(normalizePath(join(outDir, 'zuby.config.json')), JSON.stringify(zubyInternalConfig, null, 2));
|
|
@@ -72,30 +99,29 @@ export default async function build(options) {
|
|
|
72
99
|
name,
|
|
73
100
|
version,
|
|
74
101
|
}));
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
// Load the chunks manifest files
|
|
103
|
+
const clientChunksManifestPath = resolve(outDir, CLIENT_CHUNKS_MANIFEST);
|
|
104
|
+
const serverChunksManifestPath = resolve(outDir, SERVER_CHUNKS_MANIFEST);
|
|
105
|
+
const clientChunksManifest = existsSync(clientChunksManifestPath)
|
|
106
|
+
? JSON.parse(readFileSync(clientChunksManifestPath, 'utf-8'))
|
|
107
|
+
: {};
|
|
108
|
+
const serverChunksManifest = existsSync(serverChunksManifestPath)
|
|
109
|
+
? JSON.parse(readFileSync(serverChunksManifestPath, 'utf-8'))
|
|
110
|
+
: {};
|
|
111
|
+
// Run build done hook
|
|
112
|
+
// to execute plugins that are part of the build process
|
|
113
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyBuildDone, {
|
|
114
|
+
templates,
|
|
115
|
+
clientChunksManifest,
|
|
116
|
+
serverChunksManifest,
|
|
117
|
+
}, zubyPlugin => {
|
|
118
|
+
// Hide plugins that are not part of the build process
|
|
119
|
+
// from the CLI output to avoid confusion
|
|
120
|
+
if (!zubyPlugin.buildStep)
|
|
89
121
|
return;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
mkdirSync(destDir, {
|
|
93
|
-
recursive: true,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return copyFile(srcFile, destFile);
|
|
122
|
+
// Increment build step with plugin description
|
|
123
|
+
nextBuildStep(zubyPlugin.description || zubyPlugin.name);
|
|
97
124
|
});
|
|
98
|
-
await Promise.all(copyFilePromises);
|
|
99
125
|
logger?.info(`Done! 🎉`);
|
|
100
126
|
}
|
|
101
127
|
export async function getEntryFile(zubyInternalConfig) {
|
package/commands/dev.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PLUGIN_HOOKS } from '../types.js';
|
|
2
|
+
import { executePlugins, getZubyInternalConfig } from '../config.js';
|
|
2
3
|
import { performance } from 'node:perf_hooks';
|
|
3
4
|
import { getTitle } from '../branding.js';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import ZubyDevServer from '../server/zubyDevServer.js';
|
|
6
7
|
export default async function dev(options) {
|
|
7
|
-
const
|
|
8
|
-
const { customLogger: logger } =
|
|
8
|
+
const zubyInternalConfig = await getZubyInternalConfig(options.configFile);
|
|
9
|
+
const { customLogger: logger, server } = zubyInternalConfig;
|
|
9
10
|
const startTime = performance.now();
|
|
10
|
-
const { host = '127.0.0.1', port = 3000 } =
|
|
11
|
-
const zubyDevServer = new ZubyDevServer(
|
|
11
|
+
const { host = '127.0.0.1', port = 3000 } = server;
|
|
12
|
+
const zubyDevServer = new ZubyDevServer(zubyInternalConfig);
|
|
13
|
+
// Run dev setup hook
|
|
14
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyDevSetup, {
|
|
15
|
+
devServer: zubyDevServer,
|
|
16
|
+
});
|
|
12
17
|
await zubyDevServer.listen();
|
|
18
|
+
// Run dev start hook
|
|
19
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyDevStart, {
|
|
20
|
+
devServer: zubyDevServer,
|
|
21
|
+
});
|
|
13
22
|
const readyTime = performance.now();
|
|
14
23
|
logger?.info(` ${getTitle(chalk.gray(`started in ${Math.round(readyTime - startTime)}ms`))}\r\n`);
|
|
15
24
|
logger?.info(` ┃ Mode development`);
|
package/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ZubyConfig, ZubyInternalConfig } from './types.js';
|
|
1
|
+
import { PluginHook, ZubyConfig, ZubyHookParams, ZubyInternalConfig, ZubyPlugin } from './types.js';
|
|
2
|
+
import type { PluginOption as VitePluginOption, Plugin as VitePlugin } from 'vite';
|
|
2
3
|
/**
|
|
3
4
|
* Returns the path to the ZubyConfig file.
|
|
4
5
|
*/
|
|
@@ -26,8 +27,24 @@ export declare const validateConfig: (config: ZubyConfig) => void;
|
|
|
26
27
|
*/
|
|
27
28
|
export declare const mergeDefaultConfig: (config: ZubyConfig) => Promise<ZubyInternalConfig>;
|
|
28
29
|
/**
|
|
29
|
-
* This function returns the array of built-in
|
|
30
|
+
* This function returns the array of built-in plugins.
|
|
30
31
|
* Check which framework components are relying on each plugin
|
|
31
32
|
* before removing any of them.
|
|
32
33
|
*/
|
|
33
|
-
export declare const getBuiltInPlugins: () =>
|
|
34
|
+
export declare const getBuiltInPlugins: () => VitePlugin<any>[];
|
|
35
|
+
/**
|
|
36
|
+
* Executes the Zuby plugins for the given hook.
|
|
37
|
+
* @param config The ZubyConfig or ZubyInternalConfig object.
|
|
38
|
+
* @param hookName The name of the hook to execute.
|
|
39
|
+
* @param params Additional params to pass to the plugins.
|
|
40
|
+
* @param beforeHook Callback function to execute before each plugin hook.
|
|
41
|
+
* @param afterHook Callback function to execute after each plugin hook.
|
|
42
|
+
* @example executePlugins(config, 'zuby:config:setup', { config, logger })
|
|
43
|
+
*/
|
|
44
|
+
export declare const executePlugins: (config: ZubyConfig | ZubyInternalConfig, hookName: PluginHook, params?: ExecutePluginsParams, beforeHook?: (plugin: ZubyPlugin) => void | Promise<void>, afterHook?: (plugin: ZubyPlugin, hookResult: any) => void | Promise<void>) => Promise<void>;
|
|
45
|
+
export type ExecutePluginsParams = Omit<ZubyHookParams, 'command' | 'logger' | 'config'>;
|
|
46
|
+
/**
|
|
47
|
+
* Normalizes the plugins to common array format.
|
|
48
|
+
* @param plugins
|
|
49
|
+
*/
|
|
50
|
+
export declare const normalizePlugins: (plugins: (ZubyPlugin | ZubyPlugin[] | VitePluginOption | VitePluginOption[])[]) => Promise<(ZubyPlugin | VitePlugin)[]>;
|
package/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PLUGIN_HOOKS, } from './types.js';
|
|
1
2
|
import { BUILD_CHUNKS_MANIFEST, ZUBY_CONFIG_FILE } from './constants.js';
|
|
2
3
|
import { existsSync } from 'fs';
|
|
3
4
|
import { bundleRequire } from 'bundle-require';
|
|
@@ -6,8 +7,9 @@ import { createLogger } from './logger/index.js';
|
|
|
6
7
|
import contextPlugin from './plugins/contextPlugin/index.js';
|
|
7
8
|
import compileTimePlugin from './plugins/compileTimePlugin/index.js';
|
|
8
9
|
import chunkNamingPlugin from './plugins/chunkNamingPlugin/index.js';
|
|
9
|
-
import prerenderPlugin from './plugins/prerenderPlugin/index.js';
|
|
10
10
|
import manifestPlugin from './plugins/manifestPlugin/index.js';
|
|
11
|
+
import prerenderPlugin from './plugins/prerenderPlugin/index.js';
|
|
12
|
+
import standaloneBuildPlugin from './plugins/dependenciesPlugin/index.js';
|
|
11
13
|
let zubyInternalConfig;
|
|
12
14
|
/**
|
|
13
15
|
* Returns the path to the ZubyConfig file.
|
|
@@ -33,8 +35,9 @@ export const getZubyConfig = async (configFilePath) => {
|
|
|
33
35
|
const { mod } = await bundleRequire({
|
|
34
36
|
filepath: configFilePath,
|
|
35
37
|
});
|
|
36
|
-
// Find the default export
|
|
37
|
-
|
|
38
|
+
// Find the default export
|
|
39
|
+
// and wait for it to resolve if it's a promise
|
|
40
|
+
const zubyConfig = await Promise.resolve(mod.default || mod);
|
|
38
41
|
if (!zubyConfig) {
|
|
39
42
|
throw new Error(`No valid default export found in ZubyConfig file: ${configFilePath}`);
|
|
40
43
|
}
|
|
@@ -52,8 +55,12 @@ export const getZubyInternalConfig = async (configFilePath, cache = true) => {
|
|
|
52
55
|
return zubyInternalConfig;
|
|
53
56
|
const zubyConfig = await getZubyConfig(configFilePath);
|
|
54
57
|
zubyConfig.configFilePath = configFilePath;
|
|
58
|
+
// Run config setup hook
|
|
59
|
+
await executePlugins(zubyConfig, PLUGIN_HOOKS.ZubyConfigSetup);
|
|
55
60
|
validateConfig(zubyConfig);
|
|
56
61
|
zubyInternalConfig = await mergeDefaultConfig(zubyConfig);
|
|
62
|
+
// Run config done hook
|
|
63
|
+
await executePlugins(zubyInternalConfig, PLUGIN_HOOKS.ZubyConfigDone);
|
|
57
64
|
return zubyInternalConfig;
|
|
58
65
|
};
|
|
59
66
|
/**
|
|
@@ -82,6 +89,11 @@ export const mergeDefaultConfig = async (config) => {
|
|
|
82
89
|
config.cacheDir = config.cacheDir ?? '.zuby-cache';
|
|
83
90
|
config.output = config.output ?? 'static';
|
|
84
91
|
config.prerenderPaths = config.prerenderPaths ?? [];
|
|
92
|
+
config.plugins = await normalizePlugins([
|
|
93
|
+
...getBuiltInPlugins(),
|
|
94
|
+
...(config.jsx?.getPlugins() ?? []),
|
|
95
|
+
...(config.plugins ?? []),
|
|
96
|
+
]);
|
|
85
97
|
// Default server config
|
|
86
98
|
config.server = config.server ?? {};
|
|
87
99
|
config.server.host = config.server.host ?? '127.0.0.1';
|
|
@@ -116,19 +128,15 @@ export const mergeDefaultConfig = async (config) => {
|
|
|
116
128
|
config.vite.build.ssrEmitAssets = config.vite.build.ssrEmitAssets ?? true;
|
|
117
129
|
config.vite.build.modulePreload = { polyfill: false };
|
|
118
130
|
config.vite.optimizeDeps = config.vite.optimizeDeps ?? {};
|
|
119
|
-
// Merge
|
|
120
|
-
config.vite.plugins = [
|
|
121
|
-
...getBuiltInPlugins(),
|
|
122
|
-
...(config.jsx?.getPlugins() ?? []),
|
|
123
|
-
...(config.vite?.plugins ?? []),
|
|
124
|
-
];
|
|
131
|
+
// Merge Vite plugins with Zuby plugins
|
|
132
|
+
config.vite.plugins = [...(config.plugins ?? []), ...(config.vite.plugins ?? [])];
|
|
125
133
|
return {
|
|
126
134
|
...config,
|
|
127
135
|
templateExtensions: ['js', 'jsx', 'ts', 'tsx'],
|
|
128
136
|
};
|
|
129
137
|
};
|
|
130
138
|
/**
|
|
131
|
-
* This function returns the array of built-in
|
|
139
|
+
* This function returns the array of built-in plugins.
|
|
132
140
|
* Check which framework components are relying on each plugin
|
|
133
141
|
* before removing any of them.
|
|
134
142
|
*/
|
|
@@ -139,5 +147,60 @@ export const getBuiltInPlugins = () => {
|
|
|
139
147
|
chunkNamingPlugin(),
|
|
140
148
|
manifestPlugin(),
|
|
141
149
|
prerenderPlugin(),
|
|
150
|
+
standaloneBuildPlugin(),
|
|
142
151
|
];
|
|
143
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* Executes the Zuby plugins for the given hook.
|
|
155
|
+
* @param config The ZubyConfig or ZubyInternalConfig object.
|
|
156
|
+
* @param hookName The name of the hook to execute.
|
|
157
|
+
* @param params Additional params to pass to the plugins.
|
|
158
|
+
* @param beforeHook Callback function to execute before each plugin hook.
|
|
159
|
+
* @param afterHook Callback function to execute after each plugin hook.
|
|
160
|
+
* @example executePlugins(config, 'zuby:config:setup', { config, logger })
|
|
161
|
+
*/
|
|
162
|
+
export const executePlugins = async (config, hookName, params = {}, beforeHook = () => { }, afterHook = () => { }) => {
|
|
163
|
+
const logger = config.customLogger ||
|
|
164
|
+
createLogger(config.logLevel, {
|
|
165
|
+
allowClearScreen: false,
|
|
166
|
+
prefix: '[Zuby]',
|
|
167
|
+
});
|
|
168
|
+
const command = process.env.NODE_ENV === 'production' ? 'build' : 'dev';
|
|
169
|
+
const plugins = config.plugins || [];
|
|
170
|
+
for (const plugin of plugins) {
|
|
171
|
+
// Skip if the plugin is not an object
|
|
172
|
+
// or if it doesn't have hooks
|
|
173
|
+
if (!plugin || !('hooks' in plugin))
|
|
174
|
+
continue;
|
|
175
|
+
const zubyPlugin = plugin;
|
|
176
|
+
const hooks = zubyPlugin?.hooks || {};
|
|
177
|
+
const hook = hooks[hookName];
|
|
178
|
+
// Skip if the hook has invalid type
|
|
179
|
+
if (!hook || typeof hook !== 'function')
|
|
180
|
+
continue;
|
|
181
|
+
// Execute the callback function before each hook
|
|
182
|
+
await beforeHook(zubyPlugin);
|
|
183
|
+
// Execute the hook
|
|
184
|
+
const hookResult = await hook({
|
|
185
|
+
...params,
|
|
186
|
+
config,
|
|
187
|
+
logger,
|
|
188
|
+
command,
|
|
189
|
+
});
|
|
190
|
+
// Execute the callback function after each hook
|
|
191
|
+
await afterHook(zubyPlugin, hookResult);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Normalizes the plugins to common array format.
|
|
196
|
+
* @param plugins
|
|
197
|
+
*/
|
|
198
|
+
export const normalizePlugins = async (plugins) => {
|
|
199
|
+
// Resolve the plugins if they are promises
|
|
200
|
+
const resolvePlugins = await Promise.resolve(plugins);
|
|
201
|
+
return resolvePlugins
|
|
202
|
+
// Flatten the array and normalize inner arrays
|
|
203
|
+
.flatMap(plugin => (Array.isArray(plugin) ? normalizePlugins(plugin) : [plugin]))
|
|
204
|
+
// Remove false, undefined, null values
|
|
205
|
+
.filter(plugin => !!plugin);
|
|
206
|
+
};
|
package/defineConfig.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ import { ZubyConfig } from './types.js';
|
|
|
4
4
|
*
|
|
5
5
|
* It is not required, but it provides type safety and autocompletion in your IDE.
|
|
6
6
|
* See the full Zuby Configuration API Documentation
|
|
7
|
+
*
|
|
8
|
+
* @link https://zuby.futrou.com/reference/configuration
|
|
7
9
|
* @param config
|
|
8
10
|
* @example defineConfig({
|
|
9
11
|
* outDir: 'build',
|
|
10
12
|
* srcDir: './',
|
|
11
13
|
* })
|
|
12
14
|
*/
|
|
13
|
-
export declare const defineConfig: (config: ZubyConfig) => ZubyConfig
|
|
15
|
+
export declare const defineConfig: (config: ZubyConfig | (() => ZubyConfig | Promise<ZubyConfig>)) => ZubyConfig | Promise<ZubyConfig>;
|
package/defineConfig.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* It is not required, but it provides type safety and autocompletion in your IDE.
|
|
5
5
|
* See the full Zuby Configuration API Documentation
|
|
6
|
+
*
|
|
7
|
+
* @link https://zuby.futrou.com/reference/configuration
|
|
6
8
|
* @param config
|
|
7
9
|
* @example defineConfig({
|
|
8
10
|
* outDir: 'build',
|
|
@@ -10,5 +12,8 @@
|
|
|
10
12
|
* })
|
|
11
13
|
*/
|
|
12
14
|
export const defineConfig = (config) => {
|
|
15
|
+
if (typeof config === 'function') {
|
|
16
|
+
return config();
|
|
17
|
+
}
|
|
13
18
|
return config;
|
|
14
19
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
1
|
+
import { Plugin as VitePlugin } from 'vite';
|
|
2
2
|
/**
|
|
3
3
|
* This is internal plugin
|
|
4
4
|
* which configure a custom chunk naming for the build.
|
|
5
5
|
*/
|
|
6
|
-
export default function chunkNamingPlugin():
|
|
6
|
+
export default function chunkNamingPlugin(): VitePlugin;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
1
|
+
import { Plugin as VitePlugin } from 'vite';
|
|
2
2
|
type MaybePromise<T> = T | Promise<T>;
|
|
3
3
|
export type CompileTimeFunctionArgs = {
|
|
4
4
|
/** Root directory of the Vite project */
|
|
@@ -20,6 +20,6 @@ export type CompileTimeFunction = (args: CompileTimeFunctionArgs) => CompileTime
|
|
|
20
20
|
* This plugin is just modified version of vite-plugin-compile-time plugin.
|
|
21
21
|
* See: https://github.com/egoist/vite-plugin-compile-time for more details.
|
|
22
22
|
*/
|
|
23
|
-
export default function createPlugins():
|
|
23
|
+
export default function createPlugins(): VitePlugin;
|
|
24
24
|
export declare function resolveImport(importPath: string, projectRoot?: string): string | undefined;
|
|
25
25
|
export {};
|
|
@@ -17,80 +17,76 @@ export default function createPlugins() {
|
|
|
17
17
|
let useSourceMap = false;
|
|
18
18
|
const loadCache = new Map();
|
|
19
19
|
let root = process.cwd();
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
loadCache.delete(k);
|
|
33
|
-
}
|
|
20
|
+
return {
|
|
21
|
+
name: 'zuby-compile-time-plugin',
|
|
22
|
+
enforce: 'pre',
|
|
23
|
+
configResolved(config) {
|
|
24
|
+
useSourceMap = !!config.build.sourcemap;
|
|
25
|
+
root = config.root;
|
|
26
|
+
},
|
|
27
|
+
configureServer(server) {
|
|
28
|
+
server.watcher.on('all', (_, id) => {
|
|
29
|
+
for (const [k, cache] of loadCache) {
|
|
30
|
+
if (cache.watchFiles?.includes(id)) {
|
|
31
|
+
loadCache.delete(k);
|
|
34
32
|
}
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
async transform(code, id) {
|
|
38
|
-
if ((id.includes('node_modules') && !id.includes('zuby')) ||
|
|
39
|
-
!/\.(js|ts|jsx|tsx|mjs|cjs)(\?.+)?$/.test(id)) {
|
|
40
|
-
return;
|
|
41
33
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
cache.watchFiles.forEach(filepath => {
|
|
76
|
-
this.addWatchFile(filepath);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
if (cache.data !== undefined) {
|
|
80
|
-
replacement = cache.data;
|
|
81
|
-
}
|
|
82
|
-
else if (cache.code !== undefined) {
|
|
83
|
-
replacement = cache.code;
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
async transform(code, id) {
|
|
37
|
+
if ((id.includes('node_modules') && !id.includes('zuby')) ||
|
|
38
|
+
!/\.(js|ts|jsx|tsx|mjs|cjs)(\?.+)?$/.test(id)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const m = [...code.matchAll(/import\.meta\.compileTime(?:<[\w]*>)?\(['"`]([^'"`]+)['"`]\)/g)];
|
|
42
|
+
if (m.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
const s = new MagicString(code);
|
|
45
|
+
const dir = dirname(id);
|
|
46
|
+
for (const item of m) {
|
|
47
|
+
const start = item.index;
|
|
48
|
+
const end = item.index + item[0].length;
|
|
49
|
+
const importPath = item[1];
|
|
50
|
+
const filepath = resolveImport(importPath, root);
|
|
51
|
+
if (!filepath) {
|
|
52
|
+
throw new Error(`Cannot resolve module: '${importPath}'`);
|
|
53
|
+
}
|
|
54
|
+
const cacheKey = filepath;
|
|
55
|
+
let cache = loadCache.get(cacheKey);
|
|
56
|
+
if (!cache) {
|
|
57
|
+
const { mod, dependencies } = await bundleRequire({ filepath });
|
|
58
|
+
const defaultExport = mod.default || mod;
|
|
59
|
+
cache = (defaultExport && (await defaultExport({ root }))) || {};
|
|
60
|
+
cache.watchFiles = [
|
|
61
|
+
filepath,
|
|
62
|
+
...(cache.watchFiles || []),
|
|
63
|
+
...dependencies.map(p => resolve(p)),
|
|
64
|
+
];
|
|
65
|
+
if (cache.data) {
|
|
66
|
+
cache.data = uneval(cache.data);
|
|
84
67
|
}
|
|
85
|
-
|
|
68
|
+
loadCache.set(cacheKey, cache);
|
|
69
|
+
}
|
|
70
|
+
let replacement = 'null';
|
|
71
|
+
if (cache.watchFiles) {
|
|
72
|
+
cache.watchFiles.forEach(filepath => {
|
|
73
|
+
this.addWatchFile(filepath);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (cache.data !== undefined) {
|
|
77
|
+
replacement = cache.data;
|
|
78
|
+
}
|
|
79
|
+
else if (cache.code !== undefined) {
|
|
80
|
+
replacement = cache.code;
|
|
86
81
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
s.overwrite(start, end, replacement);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
code: s.toString(),
|
|
86
|
+
map: useSourceMap ? s.generateMap({ source: id }) : null,
|
|
87
|
+
};
|
|
92
88
|
},
|
|
93
|
-
|
|
89
|
+
};
|
|
94
90
|
}
|
|
95
91
|
export function resolveImport(importPath, projectRoot = process.cwd()) {
|
|
96
92
|
if (isAbsolute(importPath)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Plugin as VitePlugin } from 'vite';
|
|
2
2
|
import { Template } from '../../templates/types.js';
|
|
3
|
-
export default function index():
|
|
3
|
+
export default function index(): VitePlugin;
|
|
4
4
|
export declare function generateCompileTimeContextCode(ssr: boolean): Promise<string>;
|
|
5
5
|
export declare function generateTemplatesCode(ssr: boolean): Promise<string>;
|
|
6
6
|
export declare function generateTemplateCode(template: Template): Promise<string>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { copyFile } from 'fs/promises';
|
|
4
|
+
import { normalizePath } from '../../utils/pathUtils.js';
|
|
5
|
+
import { nodeFileTrace } from '@vercel/nft';
|
|
6
|
+
import { glob } from 'glob';
|
|
7
|
+
/**
|
|
8
|
+
* This is internal plugin
|
|
9
|
+
* that traces and copies all node_modules and other
|
|
10
|
+
* dependencies of the build.
|
|
11
|
+
*/
|
|
12
|
+
export default function standaloneBuildPlugin() {
|
|
13
|
+
return {
|
|
14
|
+
name: 'zuby-dependencies-plugin',
|
|
15
|
+
description: 'tracing dependencies...',
|
|
16
|
+
buildStep: true,
|
|
17
|
+
hooks: {
|
|
18
|
+
'zuby:build:done': async ({ config, logger }) => {
|
|
19
|
+
const { outDir } = config;
|
|
20
|
+
logger?.info(`Tracing production dependencies...`);
|
|
21
|
+
const serverFiles = await glob(`${normalizePath(outDir)}/server/**/*.js`);
|
|
22
|
+
const { fileList } = await nodeFileTrace(serverFiles, {
|
|
23
|
+
processCwd: process.cwd(),
|
|
24
|
+
ignore: (path) => {
|
|
25
|
+
return path.startsWith(outDir);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
logger?.info(`Copying ${fileList.size} files...`);
|
|
29
|
+
const copyFilePromises = [...fileList].map(async (srcFile) => {
|
|
30
|
+
const destFile = normalizePath(join(outDir, srcFile));
|
|
31
|
+
const destDir = dirname(destFile);
|
|
32
|
+
if (srcFile.startsWith(outDir) || existsSync(destFile)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!existsSync(destDir)) {
|
|
36
|
+
mkdirSync(destDir, {
|
|
37
|
+
recursive: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return copyFile(srcFile, destFile);
|
|
41
|
+
});
|
|
42
|
+
await Promise.all(copyFilePromises);
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
1
|
+
import type { Plugin as VitePlugin } from 'vite';
|
|
2
2
|
/**
|
|
3
3
|
* This is internal plugin
|
|
4
4
|
* which generated and moves required manifest files.
|
|
5
5
|
*/
|
|
6
|
-
export default function manifestPlugin():
|
|
6
|
+
export default function manifestPlugin(): VitePlugin;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ZubyPlugin } from '../../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* This is internal plugin
|
|
4
4
|
* that pre-renders the pages during the build.
|
|
5
5
|
*/
|
|
6
|
-
export default function prerenderPlugin():
|
|
6
|
+
export default function prerenderPlugin(): ZubyPlugin;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { existsSync, mkdirSync
|
|
1
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
2
2
|
import { dirname, join, relative, resolve } from 'path';
|
|
3
|
-
import { getZubyInternalConfig } from '../../config.js';
|
|
4
3
|
import chalk from 'chalk';
|
|
5
4
|
import { performance } from 'node:perf_hooks';
|
|
6
5
|
import ZubyRenderer from '../../server/zubyRenderer.js';
|
|
@@ -10,115 +9,106 @@ import { OUTPUTS } from '../../types.js';
|
|
|
10
9
|
import { PATH_TYPES } from '../../templates/types.js';
|
|
11
10
|
import { substitutePathParams } from '../../templates/pathUtils.js';
|
|
12
11
|
import { normalizePath } from '../../utils/pathUtils.js';
|
|
13
|
-
import { HTTP_HEADERS
|
|
12
|
+
import { HTTP_HEADERS } from '../../constants.js';
|
|
14
13
|
/**
|
|
15
14
|
* This is internal plugin
|
|
16
15
|
* that pre-renders the pages during the build.
|
|
17
16
|
*/
|
|
18
17
|
export default function prerenderPlugin() {
|
|
19
|
-
let viteConfig;
|
|
20
18
|
return {
|
|
21
19
|
name: 'zuby-prerender-plugin',
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const chunksManifest = existsSync(chunksManifestPath)
|
|
36
|
-
? JSON.parse(readFileSync(chunksManifestPath, 'utf-8'))
|
|
37
|
-
: {};
|
|
38
|
-
const pages = await getPages();
|
|
39
|
-
for (const page of pages) {
|
|
40
|
-
const srcFilename = normalizePath(relative(srcDir, page.filename));
|
|
41
|
-
const outFilename = chunksManifest[srcFilename]?.[0];
|
|
42
|
-
if (!outFilename) {
|
|
43
|
-
customLogger?.warn(`WARNING: Page "${page.path}" couldn't be pre-rendered because it doesn't have corresponding chunk file.`);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
const modulePath = normalizePath(resolve(outDir, 'server', outFilename.substring(1)));
|
|
47
|
-
const module = await import(`file:///${modulePath}`);
|
|
48
|
-
const pagePrerenderValue = module?.prerender && typeof module?.prerender === 'function'
|
|
49
|
-
? await module?.prerender()
|
|
50
|
-
: module?.prerender;
|
|
51
|
-
const globalPrerenderValue = output === OUTPUTS.static;
|
|
52
|
-
const shouldPrerender = pagePrerenderValue ?? globalPrerenderValue;
|
|
53
|
-
if (!shouldPrerender)
|
|
54
|
-
continue;
|
|
55
|
-
if (page.pathType === PATH_TYPES.static) {
|
|
56
|
-
prerenderPaths.push(page.path);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (!module?.prerenderPaths && pagePrerenderValue) {
|
|
60
|
-
const exampleParam = page.pathParams?.[0] || '';
|
|
61
|
-
const exampleFullPath = substitutePathParams(page.path, { [exampleParam]: 'value3' });
|
|
62
|
-
customLogger?.warn(`WARNING: Page "${page.path}" has dynamic path and enabled pre-rendering but doesn't export prerenderPaths property.`);
|
|
63
|
-
customLogger?.warn(`1. If you don't want to pre-render this page, you need to export prerender property with false value.`);
|
|
64
|
-
customLogger?.warn(`Example: export const prerender = false`);
|
|
65
|
-
customLogger?.warn(`2. If you want to pre-render this page, you need to export prerenderPaths property with array of paths.`);
|
|
66
|
-
customLogger?.warn(`Example: export const prerenderPaths = [ { "${exampleParam}" : "value" }, { "${exampleParam}" : "value2" }, "${exampleFullPath}" ]\r\n`);
|
|
67
|
-
}
|
|
68
|
-
if (!module?.prerenderPaths) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
const pagePrerenderPaths = typeof module?.prerenderPaths === 'function'
|
|
72
|
-
? await module?.prerenderPaths()
|
|
73
|
-
: module?.prerenderPaths;
|
|
74
|
-
pagePrerenderPaths.forEach((params) => {
|
|
75
|
-
// Handle case when path is defined as string
|
|
76
|
-
// Example: /products/123
|
|
77
|
-
if (typeof params === 'string') {
|
|
78
|
-
return prerenderPaths.push(params);
|
|
20
|
+
description: 'pre-rendering pages...',
|
|
21
|
+
buildStep: true,
|
|
22
|
+
hooks: {
|
|
23
|
+
'zuby:build:done': async ({ config, logger, templates, serverChunksManifest }) => {
|
|
24
|
+
const { srcDir, outDir, prerenderPaths, site, output } = config;
|
|
25
|
+
const startTime = performance.now();
|
|
26
|
+
const pages = await getPages(templates);
|
|
27
|
+
for (const page of pages) {
|
|
28
|
+
const srcFilename = normalizePath(relative(srcDir, page.filename));
|
|
29
|
+
const outFilename = serverChunksManifest[srcFilename]?.[0];
|
|
30
|
+
if (!outFilename) {
|
|
31
|
+
logger?.warn(`WARNING: Page "${page.path}" couldn't be pre-rendered because it doesn't have corresponding chunk file.`);
|
|
32
|
+
continue;
|
|
79
33
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
34
|
+
const modulePath = normalizePath(resolve(outDir, 'server', outFilename.substring(1)));
|
|
35
|
+
const module = await import(`file:///${modulePath}`);
|
|
36
|
+
const pagePrerenderValue = module?.prerender && typeof module?.prerender === 'function'
|
|
37
|
+
? await module?.prerender()
|
|
38
|
+
: module?.prerender;
|
|
39
|
+
const globalPrerenderValue = output === OUTPUTS.static;
|
|
40
|
+
const shouldPrerender = pagePrerenderValue ?? globalPrerenderValue;
|
|
41
|
+
if (!shouldPrerender)
|
|
42
|
+
continue;
|
|
43
|
+
if (page.pathType === PATH_TYPES.static) {
|
|
44
|
+
prerenderPaths.push(page.path);
|
|
45
|
+
continue;
|
|
84
46
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (existsSync(dir))
|
|
112
|
-
return;
|
|
113
|
-
mkdirSync(dir, {
|
|
114
|
-
recursive: true,
|
|
47
|
+
if (!module?.prerenderPaths && pagePrerenderValue) {
|
|
48
|
+
const exampleParam = page.pathParams?.[0] || '';
|
|
49
|
+
const exampleFullPath = substitutePathParams(page.path, { [exampleParam]: 'value3' });
|
|
50
|
+
logger?.warn(`WARNING: Page "${page.path}" has dynamic path and enabled pre-rendering but doesn't export prerenderPaths property.`);
|
|
51
|
+
logger?.warn(`1. If you don't want to pre-render this page, you need to export prerender property with false value.`);
|
|
52
|
+
logger?.warn(`Example: export const prerender = false`);
|
|
53
|
+
logger?.warn(`2. If you want to pre-render this page, you need to export prerenderPaths property with array of paths.`);
|
|
54
|
+
logger?.warn(`Example: export const prerenderPaths = [ { "${exampleParam}" : "value" }, { "${exampleParam}" : "value2" }, "${exampleFullPath}" ]\r\n`);
|
|
55
|
+
}
|
|
56
|
+
if (!module?.prerenderPaths) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const pagePrerenderPaths = typeof module?.prerenderPaths === 'function'
|
|
60
|
+
? await module?.prerenderPaths()
|
|
61
|
+
: module?.prerenderPaths;
|
|
62
|
+
pagePrerenderPaths.forEach((params) => {
|
|
63
|
+
// Handle case when path is defined as string
|
|
64
|
+
// Example: /products/123
|
|
65
|
+
if (typeof params === 'string') {
|
|
66
|
+
return prerenderPaths.push(params);
|
|
67
|
+
}
|
|
68
|
+
// Handle case when path is defined as object of params
|
|
69
|
+
// Example: { id: 123 } => /products/123
|
|
70
|
+
if (typeof params === 'object') {
|
|
71
|
+
return prerenderPaths.push(substitutePathParams(page.path, params));
|
|
72
|
+
}
|
|
115
73
|
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
74
|
+
}
|
|
75
|
+
const zubyRenderer = new ZubyRenderer(outDir);
|
|
76
|
+
await zubyRenderer.init();
|
|
77
|
+
const reqBaseUrl = `http://${site || 'localhost'}`;
|
|
78
|
+
const reqOptions = {
|
|
79
|
+
headers: {
|
|
80
|
+
[HTTP_HEADERS.UserAgent]: 'zuby-prerender',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
// Assign all discovered prerender paths to the config for other plugins.
|
|
84
|
+
config.prerenderPaths = prerenderPaths;
|
|
85
|
+
for (const path of prerenderPaths) {
|
|
86
|
+
const pageReq = new Request(new URL(path, reqBaseUrl), reqOptions);
|
|
87
|
+
const propsReq = new Request(new URL(`/_props${path}`, reqBaseUrl), reqOptions);
|
|
88
|
+
const [pageRes, propsRes] = await Promise.all([
|
|
89
|
+
zubyRenderer.render(pageReq),
|
|
90
|
+
zubyRenderer.render(propsReq),
|
|
91
|
+
]);
|
|
92
|
+
const [page, props] = await Promise.all([pageRes.text(), propsRes.text()]);
|
|
93
|
+
const pageContentType = pageRes.headers?.get('content-type');
|
|
94
|
+
const pageExtension = pageContentType?.startsWith('application/json') ? 'json' : 'html';
|
|
95
|
+
const pageTarget = join(outDir, 'client', path, `index.${pageExtension}`);
|
|
96
|
+
const propsTarget = join(outDir, 'client', '_props', path, 'index.json');
|
|
97
|
+
const pageDir = dirname(pageTarget);
|
|
98
|
+
const propsDir = dirname(propsTarget);
|
|
99
|
+
[pageDir, propsDir].forEach(dir => {
|
|
100
|
+
if (existsSync(dir))
|
|
101
|
+
return;
|
|
102
|
+
mkdirSync(dir, {
|
|
103
|
+
recursive: true,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
await Promise.all([writeFile(pageTarget, page), writeFile(propsTarget, props)]);
|
|
107
|
+
logger.info('[prerender] Pre-rendered path: ' + path);
|
|
108
|
+
}
|
|
109
|
+
const finishTime = performance.now();
|
|
110
|
+
logger?.info(chalk.green(`✓ pre-rendered in ${Math.ceil(finishTime - startTime)}ms`));
|
|
111
|
+
},
|
|
122
112
|
},
|
|
123
113
|
};
|
|
124
114
|
}
|
package/templates/index.js
CHANGED
|
@@ -98,7 +98,7 @@ export async function getTemplates(cache = true) {
|
|
|
98
98
|
const { srcDir, i18n, templateExtensions } = await getZubyInternalConfig();
|
|
99
99
|
const pagesDir = normalizePath(join(srcDir, 'pages'));
|
|
100
100
|
const files = await glob(`${pagesDir}/**/*.{${templateExtensions.join(',')}}`);
|
|
101
|
-
const locales = i18n?.locales || [];
|
|
101
|
+
const locales = (i18n?.locales || []).filter(locale => locale !== i18n?.defaultLocale);
|
|
102
102
|
const templates = files
|
|
103
103
|
.map(filename => {
|
|
104
104
|
// Normalize the path and slashes to unix style (needed for windows)
|
package/types.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import type { UserConfig as ViteUserConfig } from 'vite';
|
|
3
|
-
import type { Plugin as VitePlugin } from 'vite';
|
|
4
|
-
import type { PluginOption as VitePluginOption } from 'vite';
|
|
2
|
+
import type { UserConfig as ViteUserConfig, InlineConfig as ViteInlineConfig, PluginOption as VitePluginOption, Plugin as VitePlugin } from 'vite';
|
|
5
3
|
import { ZubyLogger } from './logger/types.js';
|
|
6
4
|
import ReadableStream = NodeJS.ReadableStream;
|
|
7
|
-
import { PathParamsType } from './templates/types.js';
|
|
5
|
+
import { PathParamsType, Template } from './templates/types.js';
|
|
6
|
+
import ZubyDevServer from './server/zubyDevServer.js';
|
|
8
7
|
export interface ZubyConfig {
|
|
9
8
|
/**
|
|
10
9
|
* The JSX provider which will be used to render the pages.
|
|
@@ -66,6 +65,11 @@ export interface ZubyConfig {
|
|
|
66
65
|
* @example ['/products/1', '/products/2']
|
|
67
66
|
*/
|
|
68
67
|
prerenderPaths?: string[];
|
|
68
|
+
/**
|
|
69
|
+
* List of plugins that use either the Zuby plugin API or Vite plugin API.
|
|
70
|
+
* @default []
|
|
71
|
+
*/
|
|
72
|
+
plugins?: (ZubyPlugin | VitePluginOption)[];
|
|
69
73
|
/**
|
|
70
74
|
* Set this option to false to disable the minification of pre-rendered HTML.
|
|
71
75
|
* @default true
|
|
@@ -126,6 +130,12 @@ export interface ZubyInternalConfig extends Required<ZubyConfig> {
|
|
|
126
130
|
* @default ['tsx', 'jsx', 'ts', 'js', 'mjs', 'md', 'mdx']
|
|
127
131
|
*/
|
|
128
132
|
templateExtensions: string[];
|
|
133
|
+
/**
|
|
134
|
+
* List of plugins that use either the Zuby plugin API or Vite plugin API
|
|
135
|
+
* in normalized format.
|
|
136
|
+
* @default []
|
|
137
|
+
*/
|
|
138
|
+
plugins: ZubyPlugin[];
|
|
129
139
|
}
|
|
130
140
|
export interface BaseCommandOptions {
|
|
131
141
|
/**
|
|
@@ -169,11 +179,9 @@ export declare const OUTPUTS: {
|
|
|
169
179
|
server: string;
|
|
170
180
|
};
|
|
171
181
|
export type Output = (typeof OUTPUTS)[keyof typeof OUTPUTS];
|
|
172
|
-
export interface ZubyPlugin extends VitePlugin {
|
|
173
|
-
}
|
|
174
182
|
export interface JsxProvider {
|
|
175
183
|
name: string;
|
|
176
|
-
getPlugins():
|
|
184
|
+
getPlugins(): (ZubyPlugin | VitePluginOption)[];
|
|
177
185
|
renderFile: string;
|
|
178
186
|
appTemplateFile: string;
|
|
179
187
|
entryTemplateFile: string;
|
|
@@ -201,3 +209,109 @@ export type PrerenderPathsItem = PathParamsType | string;
|
|
|
201
209
|
* @example export const prerenderPaths = [{ id: 1 }, '/products/2'];
|
|
202
210
|
*/
|
|
203
211
|
export type PrerenderPaths = PrerenderPathsItem[] | (() => PrerenderPathsItem[]) | (() => Promise<PrerenderPathsItem[]>);
|
|
212
|
+
export interface ZubyPlugin extends VitePlugin {
|
|
213
|
+
/**
|
|
214
|
+
* The name of the plugin.
|
|
215
|
+
* @example 'zuby-plugin-sitemap'
|
|
216
|
+
*/
|
|
217
|
+
name: string;
|
|
218
|
+
/**
|
|
219
|
+
* The description what the plugin does.
|
|
220
|
+
* This will be displayed in the CLI during the build/dev process.
|
|
221
|
+
* @example 'pre-rendering pages'
|
|
222
|
+
*/
|
|
223
|
+
description?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Set this option to true
|
|
226
|
+
* to include the plugin as build step
|
|
227
|
+
* that will be shown in the CLI during the build process.
|
|
228
|
+
* @example Step 4/4 pre-rendering pages...
|
|
229
|
+
*/
|
|
230
|
+
buildStep?: Boolean;
|
|
231
|
+
/**
|
|
232
|
+
* The Zuby plugin API hooks.
|
|
233
|
+
* Hooks allow you to run custom code at specific points during the build/dev process
|
|
234
|
+
* and modify the internal Zuby config.
|
|
235
|
+
*/
|
|
236
|
+
hooks?: {
|
|
237
|
+
/**
|
|
238
|
+
* This hook is called before the Zuby config is resolved
|
|
239
|
+
* and allows you to modify the config.
|
|
240
|
+
*/
|
|
241
|
+
'zuby:config:setup'?: (params: ZubyConfigSetupHookParams) => void | Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* This hook is called after the Zuby config is resolved by all plugins
|
|
244
|
+
* and allows you to get the final config before the build/dev process starts.
|
|
245
|
+
*/
|
|
246
|
+
'zuby:config:done'?: (params: ZubyConfigDoneHookParams) => void | Promise<void>;
|
|
247
|
+
/**
|
|
248
|
+
* This hook is called before the dev server is started
|
|
249
|
+
* and allows you to modify the server config.
|
|
250
|
+
*/
|
|
251
|
+
'zuby:dev:setup'?: (params: ZubyDevHookParams) => void | Promise<void>;
|
|
252
|
+
/**
|
|
253
|
+
* This hook is called after the dev server is started
|
|
254
|
+
*/
|
|
255
|
+
'zuby:dev:start'?: (params: ZubyDevHookParams) => void | Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* This hook is called before the build process starts
|
|
258
|
+
* and allows you to modify the vite build config.
|
|
259
|
+
*/
|
|
260
|
+
'zuby:build:setup'?: (params: ZubyBuildSetupHookParams) => void | Promise<void>;
|
|
261
|
+
/**
|
|
262
|
+
* This hook is called after the build process starts
|
|
263
|
+
*/
|
|
264
|
+
'zuby:build:start'?: (params: ZubyBuildHookParams) => void | Promise<void>;
|
|
265
|
+
/**
|
|
266
|
+
* This hook is called after the client build is done
|
|
267
|
+
*/
|
|
268
|
+
'zuby:build:client:done'?: (params: ZubyBuildHookParams) => void | Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* This hook is called after the server build is done
|
|
271
|
+
*/
|
|
272
|
+
'zuby:build:server:done'?: (params: ZubyBuildHookParams) => void | Promise<void>;
|
|
273
|
+
/**
|
|
274
|
+
* This hook is called after the whole build process is done
|
|
275
|
+
*/
|
|
276
|
+
'zuby:build:done'?: (params: ZubyBuildDoneHookParams) => void | Promise<void>;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
export interface ZubyConfigSetupHookParams {
|
|
280
|
+
config: ZubyConfig;
|
|
281
|
+
command: 'dev' | 'build';
|
|
282
|
+
}
|
|
283
|
+
export interface ZubyConfigDoneHookParams extends ZubyConfigSetupHookParams {
|
|
284
|
+
config: ZubyInternalConfig;
|
|
285
|
+
logger: ZubyLogger;
|
|
286
|
+
}
|
|
287
|
+
export interface ZubyDevHookParams {
|
|
288
|
+
config: ZubyInternalConfig;
|
|
289
|
+
logger: ZubyLogger;
|
|
290
|
+
server: ZubyDevServer;
|
|
291
|
+
}
|
|
292
|
+
export interface ZubyBuildHookParams {
|
|
293
|
+
config: ZubyInternalConfig;
|
|
294
|
+
logger: ZubyLogger;
|
|
295
|
+
templates: Template[];
|
|
296
|
+
}
|
|
297
|
+
export interface ZubyBuildSetupHookParams extends ZubyBuildHookParams {
|
|
298
|
+
clientViteBuildConfig: ViteInlineConfig;
|
|
299
|
+
serverViteBuildConfig: ViteInlineConfig;
|
|
300
|
+
}
|
|
301
|
+
export interface ZubyBuildDoneHookParams extends ZubyBuildHookParams {
|
|
302
|
+
clientChunksManifest: Record<string, string[]>;
|
|
303
|
+
serverChunksManifest: Record<string, string[]>;
|
|
304
|
+
}
|
|
305
|
+
export type ZubyHookParams = ZubyConfigSetupHookParams | ZubyConfigDoneHookParams | ZubyDevHookParams | ZubyBuildHookParams | ZubyBuildDoneHookParams;
|
|
306
|
+
export declare const PLUGIN_HOOKS: {
|
|
307
|
+
ZubyConfigSetup: string;
|
|
308
|
+
ZubyConfigDone: string;
|
|
309
|
+
ZubyDevSetup: string;
|
|
310
|
+
ZubyDevStart: string;
|
|
311
|
+
ZubyBuildSetup: string;
|
|
312
|
+
ZubyBuildStart: string;
|
|
313
|
+
ZubyBuildClientDone: string;
|
|
314
|
+
ZubyBuildServerDone: string;
|
|
315
|
+
ZubyBuildDone: string;
|
|
316
|
+
};
|
|
317
|
+
export type PluginHook = (typeof PLUGIN_HOOKS)[keyof typeof PLUGIN_HOOKS];
|
package/types.js
CHANGED
|
@@ -7,3 +7,17 @@ export const OUTPUTS = {
|
|
|
7
7
|
static: 'static',
|
|
8
8
|
server: 'server',
|
|
9
9
|
};
|
|
10
|
+
export const PLUGIN_HOOKS = {
|
|
11
|
+
// Config resolve
|
|
12
|
+
ZubyConfigSetup: 'zuby:config:setup',
|
|
13
|
+
ZubyConfigDone: 'zuby:config:done',
|
|
14
|
+
// Dev cmd
|
|
15
|
+
ZubyDevSetup: 'zuby:dev:setup',
|
|
16
|
+
ZubyDevStart: 'zuby:dev:start',
|
|
17
|
+
// Build cmd
|
|
18
|
+
ZubyBuildSetup: 'zuby:build:setup',
|
|
19
|
+
ZubyBuildStart: 'zuby:build:start',
|
|
20
|
+
ZubyBuildClientDone: 'zuby:build:client:done',
|
|
21
|
+
ZubyBuildServerDone: 'zuby:build:server:done',
|
|
22
|
+
ZubyBuildDone: 'zuby:build:done',
|
|
23
|
+
};
|