zuby 1.0.22 → 1.0.23

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 (136) hide show
  1. package/README.md +15 -24
  2. package/package.json +8 -12
  3. package/src/branding.ts +14 -0
  4. package/src/commands/build.ts +159 -0
  5. package/src/commands/dev.ts +52 -0
  6. package/src/commands/index.ts +54 -0
  7. package/src/commands/init.ts +160 -0
  8. package/src/commands/preview.ts +18 -0
  9. package/src/config.ts +146 -0
  10. package/src/context/index.ts +48 -0
  11. package/src/context/types.ts +24 -0
  12. package/{defineConfig.d.ts → src/defineConfig.ts} +4 -1
  13. package/src/examples/basic/js/components/Card/index.css +37 -0
  14. package/{examples → src/examples}/basic/js/components/Card/index.jsx +2 -4
  15. package/src/examples/basic/js/components/Card/index.module.css +36 -0
  16. package/{examples/basic/ts → src/examples/basic/js}/package.json +2 -1
  17. package/src/examples/basic/js/pages/about.jsx +9 -0
  18. package/src/examples/basic/js/pages/app.css +49 -0
  19. package/src/examples/basic/js/pages/app.jsx +5 -0
  20. package/{examples → src/examples}/basic/js/pages/index.jsx +6 -3
  21. package/src/examples/basic/js/pages/products/[id].jsx +7 -0
  22. package/src/examples/basic/js/zuby.config.mjs +7 -0
  23. package/src/examples/basic/ts/components/Card/index.module.css +36 -0
  24. package/{examples → src/examples}/basic/ts/components/Card/index.tsx +3 -9
  25. package/src/examples/basic/ts/pages/about.tsx +9 -0
  26. package/src/examples/basic/ts/pages/app.css +49 -0
  27. package/src/examples/basic/ts/pages/app.tsx +5 -0
  28. package/{examples → src/examples}/basic/ts/pages/index.tsx +6 -3
  29. package/src/examples/basic/ts/pages/products/[id].tsx +7 -0
  30. package/{examples → src/examples}/basic/ts/tsconfig.json +4 -7
  31. package/src/examples/basic/ts/zuby.config.ts +7 -0
  32. package/src/logger/index.ts +120 -0
  33. package/src/logger/types.ts +60 -0
  34. package/src/packageConfig.ts +16 -0
  35. package/src/plugins/chunkNamingPlugin/index.ts +47 -0
  36. package/src/plugins/compileTimePlugin/index.ts +136 -0
  37. package/src/plugins/contextPlugin/index.ts +65 -0
  38. package/src/plugins/prerenderPlugin/index.ts +154 -0
  39. package/{server/expressApp.js → src/server/expressApp.ts} +1 -0
  40. package/src/server/index.ts +34 -0
  41. package/src/server/renderer.ts +0 -0
  42. package/src/templates/index.ts +287 -0
  43. package/src/templates/pathUtils.ts +86 -0
  44. package/src/templates/types.ts +40 -0
  45. package/src/types.ts +197 -0
  46. package/src/utils/buildIdUtils.ts +5 -0
  47. package/{utils/pathUtils.js → src/utils/pathUtils.ts} +3 -2
  48. package/tsconfig.json +17 -0
  49. package/branding.d.ts +0 -2
  50. package/branding.js +0 -9
  51. package/commands/build.d.ts +0 -3
  52. package/commands/build.js +0 -117
  53. package/commands/dev.d.ts +0 -2
  54. package/commands/dev.js +0 -39
  55. package/commands/index.d.ts +0 -2
  56. package/commands/index.js +0 -34
  57. package/commands/init.d.ts +0 -18
  58. package/commands/init.js +0 -141
  59. package/commands/preview.d.ts +0 -2
  60. package/commands/preview.js +0 -12
  61. package/config.d.ts +0 -19
  62. package/config.js +0 -119
  63. package/constants.d.ts +0 -4
  64. package/context/index.d.ts +0 -18
  65. package/context/index.js +0 -37
  66. package/context/types.d.ts +0 -21
  67. package/context/types.js +0 -1
  68. package/defineConfig.js +0 -14
  69. package/examples/basic/js/components/Card/index.css +0 -37
  70. package/examples/basic/js/components/Card/index.module.css +0 -36
  71. package/examples/basic/js/pages/about.jsx +0 -7
  72. package/examples/basic/js/pages/app.css +0 -49
  73. package/examples/basic/js/pages/app.jsx +0 -7
  74. package/examples/basic/js/pages/products/[id].jsx +0 -7
  75. package/examples/basic/js/zuby.config.mjs +0 -6
  76. package/examples/basic/ts/components/Card/index.module.css +0 -36
  77. package/examples/basic/ts/pages/about.tsx +0 -7
  78. package/examples/basic/ts/pages/app.css +0 -49
  79. package/examples/basic/ts/pages/app.tsx +0 -9
  80. package/examples/basic/ts/pages/products/[id].tsx +0 -7
  81. package/examples/basic/ts/zuby.config.ts +0 -6
  82. package/index.js +0 -3
  83. package/logger/index.d.ts +0 -8
  84. package/logger/index.js +0 -107
  85. package/logger/types.d.ts +0 -48
  86. package/logger/types.js +0 -6
  87. package/packageConfig.d.ts +0 -1
  88. package/packageConfig.js +0 -13
  89. package/plugins/chunkNamingPlugin/index.d.ts +0 -6
  90. package/plugins/chunkNamingPlugin/index.js +0 -42
  91. package/plugins/compileTimePlugin/index.d.ts +0 -25
  92. package/plugins/compileTimePlugin/index.js +0 -105
  93. package/plugins/contextPlugin/index.d.ts +0 -6
  94. package/plugins/contextPlugin/index.js +0 -51
  95. package/plugins/prerenderPlugin/index.d.ts +0 -6
  96. package/plugins/prerenderPlugin/index.js +0 -120
  97. package/providers/index.d.ts +0 -20
  98. package/providers/index.js +0 -75
  99. package/providers/preact/index.d.ts +0 -3
  100. package/providers/preact/index.js +0 -17
  101. package/providers/preact/render.d.ts +0 -13
  102. package/providers/preact/render.js +0 -47
  103. package/providers/preact/router.d.ts +0 -5
  104. package/providers/preact/router.js +0 -37
  105. package/providers/preact/templates/app.d.ts +0 -4
  106. package/providers/preact/templates/app.js +0 -4
  107. package/providers/preact/templates/entry.d.ts +0 -2
  108. package/providers/preact/templates/entry.js +0 -11
  109. package/providers/preact/templates/error.d.ts +0 -4
  110. package/providers/preact/templates/error.js +0 -4
  111. package/providers/preact/templates/innerLayout.d.ts +0 -4
  112. package/providers/preact/templates/innerLayout.js +0 -4
  113. package/providers/preact/templates/layout.d.ts +0 -4
  114. package/providers/preact/templates/layout.js +0 -4
  115. package/providers/types.d.ts +0 -17
  116. package/providers/types.js +0 -1
  117. package/server/expressApp.cjs +0 -472
  118. package/server/expressApp.d.ts +0 -1
  119. package/server/index.d.ts +0 -1
  120. package/server/index.js +0 -657
  121. package/server/renderer.d.ts +0 -1
  122. package/server/renderer.js +0 -1
  123. package/templates/index.d.ts +0 -59
  124. package/templates/index.js +0 -254
  125. package/templates/pathUtils.d.ts +0 -13
  126. package/templates/pathUtils.js +0 -69
  127. package/templates/types.d.ts +0 -38
  128. package/templates/types.js +0 -16
  129. package/types.d.ts +0 -161
  130. package/types.js +0 -5
  131. package/utils/buildIdUtils.d.ts +0 -1
  132. package/utils/buildIdUtils.js +0 -4
  133. package/utils/pathUtils.d.ts +0 -6
  134. /package/{constants.js → src/constants.ts} +0 -0
  135. /package/{examples/basic/js → src/examples/basic/ts}/package.json +0 -0
  136. /package/{index.d.ts → src/index.ts} +0 -0
package/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Zuby.js
2
2
 
3
- Zuby.js is a simple [Preact](https://preactjs.com/) framework for building JS apps that can combine the best of the SPA and MPA worlds.
3
+ Zuby.js is a simple framework for building JS apps that combines the best of the SPA and MPA worlds.
4
4
  The framework itself is based on [Vite](https://vitejs.dev/).
5
5
 
6
+ ## Installation
7
+
8
+ You can set up new Zuby.js app easily with Zuby CLI wizard. Just run the following command and follow the instructions:
9
+
10
+ ```
11
+ npx zuby init
12
+ ```
13
+
6
14
  ## Features:
7
15
 
8
16
  - File-based routing (with similar syntax to Next.js)
@@ -14,37 +22,20 @@ The framework itself is based on [Vite](https://vitejs.dev/).
14
22
  - Fast development server
15
23
  - Built-in i18n localization support
16
24
 
17
- Missing features:
18
-
19
- - Server-side rendering
25
+ Please note that Zuby.js is still in early development, so some features may be missing or not working properly.
20
26
 
21
- ## For contributors
27
+ ## Integrations
22
28
 
23
- ### Repository setup
29
+ Zuby.js integrates with JSX libraries through the `JsxProvider` interface.
30
+ Following integrations are available:
24
31
 
25
- Please see the [NVM guide](https://github.com/nvm-sh/nvm#installing-and-updating) how to install specific Node.js version on your device.
26
- To set up this repository, run the following commands:
32
+ - [@zubyjs/preact](https://www.npmjs.com/package/@zubyjs/preact) - Preact integration for Zuby.js
27
33
 
28
- ```
29
- git clone git@gitlab.com:futrou/zuby.js.git
30
- cd zuby.js
31
- npm install
32
- ```
33
-
34
- ### Recommended versions
34
+ ## Recommended versions
35
35
 
36
36
  - Node.js: 18
37
37
  - npm: 9
38
38
 
39
- ### Releases
40
-
41
- Please follow these steps to release a new stable version of the package:
42
-
43
- 1. Check the current version in `package.json`. This version will be released. The patch version is automatically increased by CI and pushed to the master branch after each release. If you want to release a minor or major version, please change the version manually. Otherwise, you can skip this step.
44
- 2. Go to the page with [repository tags](https://gitlab.com/futrou/zuby.js/-/tags) and create a new tag from'master' with the version from `package.json` as the name (in the following format, `v1.0.0`).
45
- 3. This will trigger the CI pipeline, which will release the package, generate a changelog, and create a new release in the Gitlab UI.
46
- 4. That's it! 🎉 You can check the new release on [Gitlab releases page](https://gitlab.com/futrou/zuby.js/-/releases) and [NPM package page](https://www.npmjs.com/package/zuby).
47
-
48
39
  ## License
49
40
 
50
41
  MIT
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "zuby",
3
- "version": "1.0.22",
4
- "description": "Zuby.js is Preact Framework for building SPA apps using Vite",
3
+ "version": "1.0.23",
4
+ "description": "Zuby.js is framework for building SPA apps using Vite",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "scripts": {
8
8
  "release": "npm publish --access public ./dist/ ",
9
- "bump-version": "npm version patch",
10
9
  "build": "rm -rf dist/ && tsc && cp -rf package.json README.md src/examples dist/ && npm run bundle-server && npm run bundle-express",
11
10
  "bundle-server": "esbuild src/server/index.ts --bundle --platform=node --format=esm --outfile=dist/server/index.js",
12
11
  "bundle-express": "esbuild src/server/expressApp.ts --bundle --platform=node --format=cjs --minify --outfile=dist/server/expressApp.cjs",
13
12
  "watch-build": "npm run build && tsc --watch",
14
13
  "push-build": "npm run build && cd dist && npm link && cd ..",
15
- "prettier": "prettier --write ./**/*",
16
- "test": "echo \"Error: no test specified\" && exit 1"
14
+ "test": "exit 0"
15
+ },
16
+ "publishConfig": {
17
+ "directory": "dist",
18
+ "linkDirectory": true
17
19
  },
18
20
  "dependencies": {
19
21
  "@preact/preset-vite": "^2.6.0",
@@ -25,6 +27,7 @@
25
27
  "esbuild": "^0.19.5",
26
28
  "glob": "^10.3.10",
27
29
  "inquirer": "^9.2.11",
30
+ "jest": "^29.7.0",
28
31
  "magic-string": "^0.30.5",
29
32
  "rollup": "^4.0.2",
30
33
  "vite": "^4.4.11",
@@ -82,12 +85,5 @@
82
85
  ],
83
86
  "engines": {
84
87
  "node": ">=16"
85
- },
86
- "prettier": {
87
- "singleQuote": true,
88
- "semi": true,
89
- "trailingComma": "es5",
90
- "printWidth": 100,
91
- "arrowParens": "avoid"
92
88
  }
93
89
  }
@@ -0,0 +1,14 @@
1
+ import chalk from 'chalk';
2
+ import { getZubyPackageConfig } from './packageConfig.js';
3
+
4
+ const { name, version } = getZubyPackageConfig();
5
+
6
+ export const getBrand = () => {
7
+ return name.charAt(0).toUpperCase() + name.slice(1) + '.js';
8
+ };
9
+
10
+ export const getTitle = (title: string) => {
11
+ return `${chalk.bgYellow.bold.whiteBright(` ${getBrand()} `)}${chalk.yellowBright.bgWhite(
12
+ ` v${version} `
13
+ )} ${title}`;
14
+ };
@@ -0,0 +1,159 @@
1
+ import { BuildCommandOptions, MODES, ZubyConfig } from '../types.js';
2
+ import { getZubyConfig } from '../config.js';
3
+ import chalk from 'chalk';
4
+ import { getTitle } from '../branding.js';
5
+ import { build as viteBuild } from 'vite';
6
+ import { dirname, join } from 'path';
7
+ import { normalizePath } from '../utils/pathUtils.js';
8
+ import { existsSync, rmSync, writeFileSync, copyFileSync, mkdirSync } from 'fs';
9
+ import { ZUBY_CONFIG_FILE } from '../constants.js';
10
+ import { TEMPLATES } from '../templates/types.js';
11
+ import { glob } from 'glob';
12
+ import { fileURLToPath } from 'url';
13
+ import { getZubyPackageConfig } from '../packageConfig.js';
14
+ import { nodeFileTrace } from '@vercel/nft';
15
+ import { copyFile } from 'fs/promises';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+
20
+ export default async function build(options: BuildCommandOptions) {
21
+ const zubyConfig = await getZubyConfig(options.configFile);
22
+ const { vite: viteConfig, customLogger: logger, outDir = '' } = zubyConfig;
23
+
24
+ process.env.NODE_ENV = MODES.production;
25
+ logger?.info(getTitle(chalk.gray(`building for production...`)));
26
+
27
+ // Clean build directory
28
+ if (existsSync(outDir || '')) {
29
+ rmSync(outDir || '', {
30
+ recursive: true,
31
+ });
32
+ }
33
+
34
+ // Load the entry file
35
+ const entryFile = await getEntryFile(zubyConfig);
36
+
37
+ // Client build
38
+ logger?.info(
39
+ `${chalk.bgYellow.bold.whiteBright(` Step 1/4 `)} ${chalk.gray(`building client...`)}`
40
+ );
41
+ await viteBuild({
42
+ configFile: false,
43
+ ...viteConfig,
44
+ build: {
45
+ ...viteConfig?.build,
46
+ outDir: normalizePath(join(outDir, 'client')),
47
+ rollupOptions: {
48
+ ...viteConfig?.build?.rollupOptions,
49
+ input: {
50
+ entry: normalizePath(entryFile),
51
+ },
52
+ },
53
+ },
54
+ mode: MODES.production,
55
+ });
56
+
57
+ // Server build
58
+ logger?.info(
59
+ `${chalk.bgYellow.bold.whiteBright(` Step 2/4 `)} ${chalk.gray(`building server...`)}`
60
+ );
61
+ await viteBuild({
62
+ configFile: false,
63
+ ...viteConfig,
64
+ build: {
65
+ ...viteConfig?.build,
66
+ outDir: normalizePath(join(outDir, 'server')),
67
+ ssr: normalizePath(entryFile),
68
+ ssrManifest: true,
69
+ },
70
+ optimizeDeps: {
71
+ ...viteConfig?.optimizeDeps,
72
+ entries: ['**/*'],
73
+ include: ['**/*'],
74
+ force: true,
75
+ disabled: false,
76
+ },
77
+ mode: MODES.production,
78
+ });
79
+
80
+ // Add serialized zuby config to build directory
81
+ writeFileSync(
82
+ normalizePath(join(outDir, ZUBY_CONFIG_FILE.replace(/\.mjs$/, '.json'))),
83
+ JSON.stringify(zubyConfig, null, 2)
84
+ );
85
+
86
+ // Add standalone server assets
87
+ copyFileSync(
88
+ normalizePath(join(__dirname, '..', 'server', 'index.js')),
89
+ normalizePath(join(outDir, 'server.mjs'))
90
+ );
91
+ copyFileSync(
92
+ normalizePath(join(__dirname, '..', 'server', 'expressApp.cjs')),
93
+ normalizePath(join(outDir, 'expressApp.cjs'))
94
+ );
95
+
96
+ const { name, version } = getZubyPackageConfig();
97
+
98
+ // Write package.json
99
+ writeFileSync(
100
+ normalizePath(join(outDir, 'package.json')),
101
+ JSON.stringify({
102
+ type: 'module',
103
+ name,
104
+ version,
105
+ })
106
+ );
107
+
108
+ logger?.info(
109
+ `${chalk.bgYellow.bold.whiteBright(` Step 4/4 `)} ${chalk.gray(`tracing dependencies...`)}`
110
+ );
111
+
112
+ logger?.info(`Tracing production dependencies...`);
113
+ const { fileList } = await nodeFileTrace([normalizePath(join(outDir, 'server', 'entry.js'))], {
114
+ processCwd: process.cwd(),
115
+ ignore: (path: string) => {
116
+ return path.startsWith(outDir);
117
+ },
118
+ });
119
+
120
+ logger?.info(`Copying ${fileList.size} files...`);
121
+ const copyFilePromises = [...fileList].map(async (srcFile: string) => {
122
+ const destFile = normalizePath(join(outDir, srcFile));
123
+ const destDir = dirname(destFile);
124
+ if (srcFile.startsWith(outDir) || existsSync(destFile)) {
125
+ return;
126
+ }
127
+ if (!existsSync(destDir)) {
128
+ mkdirSync(destDir, {
129
+ recursive: true,
130
+ });
131
+ }
132
+ return copyFile(srcFile, destFile);
133
+ });
134
+ await Promise.all(copyFilePromises);
135
+
136
+ logger?.info(`Done! 🎉`);
137
+ }
138
+
139
+ export async function getEntryFile(zubyConfig: ZubyConfig) {
140
+ const { srcDir = '', pageExtensions = [], jsx, customLogger: logger } = zubyConfig;
141
+
142
+ const userEntryFile = (
143
+ await glob(`${normalizePath(srcDir)}${TEMPLATES.entry}.{${pageExtensions.join(',')}}`)
144
+ ).pop();
145
+ const entryFile = userEntryFile || jsx?.entryTemplateFile;
146
+
147
+ if (!entryFile) {
148
+ logger?.error(
149
+ `No entry template is available. Please create a file named '${TEMPLATES.entry}' in your project directory or use different jsx.`
150
+ );
151
+ process.exit(1);
152
+ }
153
+
154
+ if (userEntryFile) {
155
+ logger?.info(chalk.gray(`Using custom entry template: '${userEntryFile}'`));
156
+ }
157
+
158
+ return entryFile;
159
+ }
@@ -0,0 +1,52 @@
1
+ import { MODES, ServerCommandOptions } from '../types.js';
2
+ import { getZubyConfig } from '../config.js';
3
+ import { createServer, InlineConfig, preview, PreviewServer, ViteDevServer } from 'vite';
4
+ import { normalizePath } from '../utils/pathUtils.js';
5
+ import { join } from 'path';
6
+ import { performance } from 'node:perf_hooks';
7
+ import { getTitle } from '../branding.js';
8
+ import chalk from 'chalk';
9
+
10
+ export default async function dev(options: ServerCommandOptions) {
11
+ const {
12
+ vite: viteConfig,
13
+ customLogger: logger,
14
+ outDir = '',
15
+ } = await getZubyConfig(options.configFile);
16
+
17
+ const port = options.port ? Number(options.port) : viteConfig?.server?.port;
18
+ const host = options.host || viteConfig?.server?.host;
19
+ const mode = MODES.development;
20
+
21
+ const serverConfig: InlineConfig = {
22
+ ...viteConfig,
23
+ server: {
24
+ ...viteConfig?.server,
25
+ port,
26
+ host,
27
+ },
28
+ build: {
29
+ outDir: normalizePath(join(outDir, 'client')),
30
+ },
31
+ mode,
32
+ };
33
+
34
+ const startTime = performance.now();
35
+ const server = await createServer(serverConfig);
36
+ await server.listen();
37
+ const readyTime = performance.now();
38
+
39
+ if (!server.httpServer) {
40
+ throw new Error('HTTP server not available');
41
+ }
42
+
43
+ const bindAddress = server.httpServer.address();
44
+ const bindPort = typeof bindAddress === 'string' ? undefined : bindAddress?.port;
45
+
46
+ logger?.info(
47
+ ` ${getTitle(chalk.gray(`started in ${Math.round(readyTime - startTime)}ms`))}\r\n`
48
+ );
49
+ logger?.info(` ┃ Mode ${mode}`);
50
+ logger?.info(` ┃ Local http://${host}:${bindPort || port}/`);
51
+ logger?.info(` ┃ Network ${chalk.gray('use --host and --port to expose')}\r\n`);
52
+ }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { BuildCommandOptions, InitCommandOptions, ServerCommandOptions } from '../types.js';
4
+ import { getZubyPackageConfig } from '../packageConfig.js';
5
+
6
+ import dev from './dev.js';
7
+ import build from './build.js';
8
+ import preview from './preview.js';
9
+ import init from './init.js';
10
+
11
+ const { name, description, version } = getZubyPackageConfig();
12
+ const program = new Command().name(name).description(description).version(version);
13
+
14
+ program
15
+ .command('dev')
16
+ .description('Starts the development server')
17
+ .option('-p, --port <port>', 'Port to use for the dev server', '3000')
18
+ .option('-h, --host <host>', 'Host to use for the dev server', 'localhost')
19
+ .option(
20
+ '-c, --config-file <file>',
21
+ 'The relative path to file with Zuby config',
22
+ 'zuby.config.mjs'
23
+ )
24
+ .action(async options => dev(options as ServerCommandOptions));
25
+
26
+ program
27
+ .command('preview')
28
+ .alias('start')
29
+ .description('Starts the preview server for the production build')
30
+ .option('-p, --port <port>', 'Port to use for the dev server', '3000')
31
+ .option('-h, --host <host>', 'Host to use for the dev server', 'localhost')
32
+ .option(
33
+ '-c, --config-file <file>',
34
+ 'The relative path to file with Zuby config',
35
+ 'zuby.config.mjs'
36
+ )
37
+ .action(async options => preview(options as ServerCommandOptions));
38
+
39
+ program
40
+ .command('build')
41
+ .description('Builds the app for production')
42
+ .option(
43
+ '-c, --config-file <file>',
44
+ 'The relative path to file with Zuby config',
45
+ 'zuby.config.mjs'
46
+ )
47
+ .action(async options => build(options as BuildCommandOptions));
48
+
49
+ program
50
+ .command('init')
51
+ .description('Initializes a new Zuby project')
52
+ .action(async options => init(options as InitCommandOptions));
53
+
54
+ program.parse(process.argv);
@@ -0,0 +1,160 @@
1
+ import { InitCommandOptions } from '../types.js';
2
+ import inquirer from 'inquirer';
3
+ import { createLogger } from '../logger/index.js';
4
+ import { getTitle } from '../branding.js';
5
+ import chalk from 'chalk';
6
+ import { existsSync, readdirSync, cpSync, statSync } from 'fs';
7
+ import { readFile, writeFile } from 'fs/promises';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join } from 'path';
10
+ import { normalizePath } from '../utils/pathUtils.js';
11
+ import { getZubyPackageConfig } from '../packageConfig.js';
12
+ import { globSync } from 'glob';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ export default async function init(options: InitCommandOptions) {
18
+ const logger = createLogger();
19
+ logger.clearScreen('info');
20
+
21
+ logger?.info(`${getTitle(`Let's get started with Zuby`)}\r\n`);
22
+ logger.info('Initializing a new Zuby project');
23
+
24
+ const examples = getExamples();
25
+ const defaultProjectName = 'my-zuby-app';
26
+ const defaultExample = examples.find(example => example.name === 'basic');
27
+
28
+ const { projectPath, jsxProviderName, exampleName } = await inquirer.prompt([
29
+ {
30
+ type: 'input',
31
+ name: 'projectPath',
32
+ message: 'Where would you like to create your new project?',
33
+ default: `./${defaultProjectName}`,
34
+ prefix: `${chalk.yellowBright.bgWhite(' name ')} ❯ `,
35
+ validate(projectPath: string): boolean | string {
36
+ if (existsSync(projectPath)) {
37
+ return 'Project with this name already exists. Please choose a different one.';
38
+ }
39
+ return true;
40
+ },
41
+ },
42
+ {
43
+ type: 'list',
44
+ name: 'jsxProviderName',
45
+ message: 'In which JSX framework would you like to write your components?',
46
+ choices: ['preact', 'react'],
47
+ default: 'preact',
48
+ prefix: `${chalk.yellowBright.bgWhite(' jsx ')} ❯ `,
49
+ },
50
+ {
51
+ type: 'list',
52
+ name: 'exampleName',
53
+ message: 'Which Zuby example project would you like to use?',
54
+ choices: examples.map(example => example.name),
55
+ default: defaultExample?.name,
56
+ prefix: `${chalk.yellowBright.bgWhite(' example ')} ❯ `,
57
+ },
58
+ ]);
59
+
60
+ let useTypescript = false;
61
+ const selectedExample = examples.find(example => example.name === exampleName);
62
+ if (!selectedExample) {
63
+ logger.error(`Example ${exampleName} was not found`);
64
+ process.exit(1);
65
+ }
66
+
67
+ if (selectedExample?.isTs) {
68
+ ({ useTypescript } = await inquirer.prompt([
69
+ {
70
+ type: 'confirm',
71
+ name: 'useTypescript',
72
+ message: 'Do you want to write in TypeScript?',
73
+ default: true,
74
+ prefix: `${chalk.yellowBright.bgWhite(' ts ')} ❯ `,
75
+ },
76
+ ]));
77
+ }
78
+
79
+ const projectName = projectPath.split('/').pop() || defaultProjectName;
80
+ const zubyVersion = getZubyPackageConfig().version;
81
+
82
+ await initExample(selectedExample, {
83
+ projectName,
84
+ projectPath,
85
+ jsxProviderName,
86
+ useTypescript,
87
+ zubyVersion,
88
+ });
89
+
90
+ logger?.info(`\r\nCreated ${chalk.bold(projectName)} at ${chalk.bold(projectPath)}`);
91
+ logger?.info(`${chalk.greenBright.bold('We are done! 🎉')}`);
92
+ logger?.info(`\r\n`);
93
+ logger?.info(`Now enter your project directory using ${chalk.bold(`cd ${projectPath}`)}`);
94
+ logger?.info(
95
+ `and run ${chalk.bold('npm install')} or ${chalk.bold(
96
+ 'yarn install'
97
+ )} to install all dependencies.`
98
+ );
99
+ }
100
+
101
+ export async function initExample(
102
+ example: Example,
103
+ // Following values can be used in templates
104
+ // and will be replaced during init.
105
+ // Example: <jsxProviderName> => preact
106
+ options: InitOptions
107
+ ) {
108
+ const { projectPath = '', useTypescript } = options;
109
+
110
+ // Copy example files
111
+ cpSync(normalizePath(join(example.path, useTypescript ? 'ts' : 'js')), projectPath, {
112
+ recursive: true,
113
+ });
114
+
115
+ // Interpolate values in files of the example
116
+ const transformPromises = globSync(normalizePath(join(projectPath, '**', '*'))).map(
117
+ async file => {
118
+ if (statSync(file).isDirectory()) return;
119
+ let fileContent = await readFile(file, 'utf-8');
120
+
121
+ // Remove template comments
122
+ fileContent = fileContent.replace(/\/\/\s+@template\s+/, '$1');
123
+
124
+ // Replace all available <key> values in the file
125
+ Object.entries(options).forEach(([key, value]) => {
126
+ fileContent = fileContent.replace(new RegExp(`<${key}>`, 'g'), value.toString());
127
+ });
128
+
129
+ await writeFile(file, fileContent);
130
+ }
131
+ );
132
+
133
+ await Promise.all(transformPromises);
134
+ }
135
+
136
+ export function getExamples(): Example[] {
137
+ const examplesPath = normalizePath(join(__dirname, '..', 'examples'));
138
+ const exampleNames = readdirSync(examplesPath);
139
+ return exampleNames.map(exampleName => ({
140
+ name: exampleName,
141
+ path: normalizePath(join(examplesPath, exampleName)),
142
+ isJs: existsSync(normalizePath(join(examplesPath, exampleName, 'js'))),
143
+ isTs: existsSync(normalizePath(join(examplesPath, exampleName, 'ts'))),
144
+ }));
145
+ }
146
+
147
+ export interface Example {
148
+ name: string;
149
+ path: string;
150
+ isJs: boolean;
151
+ isTs: boolean;
152
+ }
153
+
154
+ export interface InitOptions {
155
+ projectName?: string;
156
+ projectPath?: string;
157
+ jsxProviderName?: string;
158
+ useTypescript?: boolean;
159
+ zubyVersion?: string;
160
+ }
@@ -0,0 +1,18 @@
1
+ import { ServerCommandOptions } from '../types.js';
2
+ import { getZubyConfig } from '../config.js';
3
+ import { existsSync } from 'fs';
4
+ import { normalizePath } from '../utils/pathUtils.js';
5
+ import { resolve } from 'path';
6
+
7
+ export default async function preview(options: ServerCommandOptions) {
8
+ const { outDir = '' } = await getZubyConfig(options.configFile);
9
+
10
+ if (outDir && !existsSync(outDir)) {
11
+ throw new Error(
12
+ `The outDir '${outDir}' does not exist. Did you forget to run 'zuby build' first?`
13
+ );
14
+ }
15
+
16
+ const serverPath = normalizePath(resolve(outDir, 'server.mjs'));
17
+ await import(`file:///${serverPath}`);
18
+ }