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 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, mkdirSync } from 'fs';
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 { nodeFileTrace } from '@vercel/nft';
14
- import { copyFile } from 'fs/promises';
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, srcDir, cacheDir, publicDir, configFilePath, } = zubyInternalConfig;
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
- // Clean build directory
23
- if (existsSync(outDir || '')) {
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
- // Client build
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
- // Server build
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
- logger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 4/4 `)} ${chalk.gray(`tracing dependencies...`)}`);
76
- logger?.info(`Tracing production dependencies...`);
77
- const serverFiles = await glob(`${normalizePath(outDir)}/server/**/*.js`);
78
- const { fileList } = await nodeFileTrace(serverFiles, {
79
- processCwd: process.cwd(),
80
- ignore: (path) => {
81
- return path.startsWith(outDir);
82
- },
83
- });
84
- logger?.info(`Copying ${fileList.size} files...`);
85
- const copyFilePromises = [...fileList].map(async (srcFile) => {
86
- const destFile = normalizePath(join(outDir, srcFile));
87
- const destDir = dirname(destFile);
88
- if (srcFile.startsWith(outDir) || existsSync(destFile)) {
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
- if (!existsSync(destDir)) {
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 { getZubyInternalConfig } from '../config.js';
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 zubyConfig = await getZubyInternalConfig(options.configFile);
8
- const { customLogger: logger } = zubyConfig;
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 } = zubyConfig.server;
11
- const zubyDevServer = new ZubyDevServer(zubyConfig);
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 Zuby plugins.
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: () => (false | import("vite").Plugin<any> | import("vite").PluginOption[] | Promise<false | import("vite").Plugin<any> | import("vite").PluginOption[] | null | undefined> | null | undefined)[];
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 and cache the config
37
- const zubyConfig = mod.default || mod;
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 built-in plugins with user plugins
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 Zuby plugins.
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
1
  {
2
2
  "name": "zuby",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
4
4
  "description": "Zuby.js is framework for building SPA apps using Vite",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -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(): Plugin;
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(): Plugin[];
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
- name: 'zuby-compile-time-plugin',
23
- enforce: 'pre',
24
- configResolved(config) {
25
- useSourceMap = !!config.build.sourcemap;
26
- root = config.root;
27
- },
28
- configureServer(server) {
29
- server.watcher.on('all', (_, id) => {
30
- for (const [k, cache] of loadCache) {
31
- if (cache.watchFiles?.includes(id)) {
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
- const m = [
43
- ...code.matchAll(/import\.meta\.compileTime(?:<[\w]*>)?\(['"`]([^'"`]+)['"`]\)/g),
44
- ];
45
- if (m.length === 0)
46
- return;
47
- const s = new MagicString(code);
48
- const dir = dirname(id);
49
- for (const item of m) {
50
- const start = item.index;
51
- const end = item.index + item[0].length;
52
- const importPath = item[1];
53
- const filepath = resolveImport(importPath, root);
54
- if (!filepath) {
55
- throw new Error(`Cannot resolve module: '${importPath}'`);
56
- }
57
- const cacheKey = filepath;
58
- let cache = loadCache.get(cacheKey);
59
- if (!cache) {
60
- const { mod, dependencies } = await bundleRequire({ filepath });
61
- const defaultExport = mod.default || mod;
62
- cache = (defaultExport && (await defaultExport({ root }))) || {};
63
- cache.watchFiles = [
64
- filepath,
65
- ...(cache.watchFiles || []),
66
- ...dependencies.map(p => resolve(p)),
67
- ];
68
- if (cache.data) {
69
- cache.data = uneval(cache.data);
70
- }
71
- loadCache.set(cacheKey, cache);
72
- }
73
- let replacement = 'null';
74
- if (cache.watchFiles) {
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
- s.overwrite(start, end, replacement);
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
- return {
88
- code: s.toString(),
89
- map: useSourceMap ? s.generateMap({ source: id }) : null,
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 { PluginOption } from 'vite';
1
+ import type { Plugin as VitePlugin } from 'vite';
2
2
  import { Template } from '../../templates/types.js';
3
- export default function index(): PluginOption;
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,7 @@
1
+ import { ZubyPlugin } from '../../types.js';
2
+ /**
3
+ * This is internal plugin
4
+ * that traces and copies all node_modules and other
5
+ * dependencies of the build.
6
+ */
7
+ export default function standaloneBuildPlugin(): ZubyPlugin;
@@ -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(): Plugin;
6
+ export default function manifestPlugin(): VitePlugin;
@@ -1,6 +1,6 @@
1
- import { Plugin } from 'vite';
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(): Plugin;
6
+ export default function prerenderPlugin(): ZubyPlugin;
@@ -1,6 +1,5 @@
1
- import { existsSync, mkdirSync, readFileSync } from 'fs';
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, SERVER_CHUNKS_MANIFEST } from '../../constants.js';
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
- apply: 'build',
23
- enforce: 'post',
24
- async configResolved(config) {
25
- viteConfig = config;
26
- },
27
- async closeBundle() {
28
- // If the build is not for SSR, then skip the pre-rendering
29
- if (!viteConfig?.build.ssr) {
30
- return;
31
- }
32
- const startTime = performance.now();
33
- const { srcDir, outDir, customLogger, prerenderPaths, site, output } = await getZubyInternalConfig();
34
- const chunksManifestPath = resolve(outDir, SERVER_CHUNKS_MANIFEST);
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
- // Handle case when path is defined as object of params
81
- // Example: { id: 123 } => /products/123
82
- if (typeof params === 'object') {
83
- return prerenderPaths.push(substitutePathParams(page.path, params));
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
- customLogger?.info(`${chalk.bgYellow.bold.whiteBright(` Step 3/4 `)} ${chalk.gray(`pre-rendering pages...`)}`);
88
- const zubyRenderer = new ZubyRenderer(outDir);
89
- await zubyRenderer.init();
90
- const reqBaseUrl = `http://${site || 'localhost'}`;
91
- const reqOptions = {
92
- headers: {
93
- [HTTP_HEADERS.UserAgent]: 'zuby-prerender',
94
- },
95
- };
96
- for (const path of prerenderPaths) {
97
- const pageReq = new Request(new URL(path, reqBaseUrl), reqOptions);
98
- const propsReq = new Request(new URL(`/_props${path}`, reqBaseUrl), reqOptions);
99
- const [pageRes, propsRes] = await Promise.all([
100
- zubyRenderer.render(pageReq),
101
- zubyRenderer.render(propsReq),
102
- ]);
103
- const [page, props] = await Promise.all([pageRes.text(), propsRes.text()]);
104
- const pageContentType = pageRes.headers?.get('content-type');
105
- const pageExtension = pageContentType?.startsWith('application/json') ? 'json' : 'html';
106
- const pageTarget = join(outDir, 'client', path, `index.${pageExtension}`);
107
- const propsTarget = join(outDir, 'client', '_props', path, 'index.json');
108
- const pageDir = dirname(pageTarget);
109
- const propsDir = dirname(propsTarget);
110
- [pageDir, propsDir].forEach(dir => {
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
- await Promise.all([writeFile(pageTarget, page), writeFile(propsTarget, props)]);
118
- customLogger.info('[prerender] Pre-rendered path: ' + path);
119
- }
120
- const finishTime = performance.now();
121
- customLogger?.info(chalk.green(`✓ pre-rendered in ${Math.ceil(finishTime - startTime)}ms`));
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
  }
@@ -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(): VitePluginOption[] | VitePluginOption[][];
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
+ };