diff --git a/packages/zely-js-cli/commands/start.ts b/packages/zely-js-cli/commands/start.ts new file mode 100644 index 00000000..0de82fd6 --- /dev/null +++ b/packages/zely-js-cli/commands/start.ts @@ -0,0 +1,5 @@ +import { dev } from './dev'; + +export async function start() { + await dev(); +} diff --git a/packages/zely-js-cli/src/index.ts b/packages/zely-js-cli/src/index.ts index 4d7d42b3..1afed354 100644 --- a/packages/zely-js-cli/src/index.ts +++ b/packages/zely-js-cli/src/index.ts @@ -2,10 +2,12 @@ import animaux from 'animaux'; +import { info } from '@zely-js/logger'; import pkg from '../package.json'; import { dev } from '../commands/dev'; import { build } from '../commands/build'; import { init } from '../commands/init'; +import { start } from '../commands/start'; const app = animaux('zely-js'); @@ -27,12 +29,21 @@ app.command('build').action(async () => { await build(); }); +app.command('start').action(async () => { + // production mode + process.env.NODE_ENV = 'production'; + process.env.ZELY_WORKING_FRAMEWORK = 'zely-cli'; + + await start(); +}); + app .command('init') .option('--dir, -d', 'Provide output directory.') .option('--template, -t', 'Template (typescript/javascript)', 'typescript') .action(async (options) => { // console.log(options); + info(`cloning template: ${options.template || 'typescript'}`); await init(options.dir || '.', options.template || 'typescript'); }); diff --git a/packages/zely-js-core/src/controller/index.ts b/packages/zely-js-core/src/controller/index.ts index 383deb43..3096dc32 100644 --- a/packages/zely-js-core/src/controller/index.ts +++ b/packages/zely-js-core/src/controller/index.ts @@ -125,19 +125,22 @@ export class PageCache { constructor(page: Page[], config: UserConfig) { const loader = createLoader(config); - if (existsSync(join(config.cwd || process.cwd(), config.dist || '.zely'))) { - rmSync(join(config.cwd || process.cwd(), config.dist || '.zely'), { + if (config.keepDist !== true) { + if (existsSync(join(config.cwd || process.cwd(), config.dist || '.zely'))) { + rmSync(join(config.cwd || process.cwd(), config.dist || '.zely'), { + recursive: true, + }); + } + + mkdirSync(join(config.cwd || process.cwd(), config.dist || '.zely'), { + recursive: true, + }); + } else if (!existsSync(join(config.cwd || process.cwd(), config.dist || '.zely'))) { + mkdirSync(join(config.cwd || process.cwd(), config.dist || '.zely'), { recursive: true, }); } - mkdirSync(join(config.cwd || process.cwd(), config.dist || '.zely'), { - recursive: true, - }); - mkdirSync(join(config.cwd || process.cwd(), config.dist || '.zely', '_pages'), { - recursive: true, - }); - writeFileSync(HASH_DIRECTORY(config), '{}'); this.#modules = page; @@ -145,6 +148,30 @@ export class PageCache { this.config = config; } + async productionBuild() { + const base = join(this.config.cwd || process.cwd(), 'pages'); + + if (process.env.NODE_ENV === 'production') { + for await (const page of this.#modules) { + const output = await this.loader(join(base, page.filename), { + type: 'page', + buildOptions: {}, + }); + + page.module.data = getValue(output.module); + page.module.builtPath = output.filename; + page.module.builtMapPath = output.map; + page.module.type = isExportDefault(output.module) ? 'export-default' : 'export'; + page.module.isLoaded = true; + page.id = performance.now(); + + this.writeIdMap({ ...this.readIdMap(), [page.filename]: page.id }); + } + } else { + throw new Error('production build is only available in production mode.'); + } + } + // id map writeIdMap(data: any) { writeFileSync(HASH_DIRECTORY(this.config), JSON.stringify(data)); diff --git a/packages/zely-js-core/src/index.ts b/packages/zely-js-core/src/index.ts index 68974822..2ca292e9 100644 --- a/packages/zely-js-core/src/index.ts +++ b/packages/zely-js-core/src/index.ts @@ -2,3 +2,4 @@ export { createZelyServer } from './server'; export { controll } from './controller'; export { methods } from './controller/methods'; export * from './methods'; +export * from './production'; diff --git a/packages/zely-js-core/src/production.ts b/packages/zely-js-core/src/production.ts new file mode 100644 index 00000000..ce97e1c2 --- /dev/null +++ b/packages/zely-js-core/src/production.ts @@ -0,0 +1,54 @@ +import { error } from '@zely-js/logger'; +import { pathToRegexp } from '@zept/path-regexp'; + +import { performance } from 'node:perf_hooks'; +import { join, relative } from 'node:path'; + +import { readDirectory } from '~/zely-js-core/lib/read-directory'; +import type { UserConfig } from '~/zely-js-core'; +import { transformFilename } from '~/zely-js-core/lib/file-to-path'; +import { removeExtension } from '~/zely-js-core/lib/ext'; + +import { PageCache } from './controller'; +import { filenameToRoute } from './server'; + +export async function productionBuild(options: UserConfig) { + // exit early if no options provided + if (!options) { + error(new Error('config must be provided')); + process.exit(1); + } + + const files = readDirectory(join(options.cwd || process.cwd(), 'pages')); + const pages = new PageCache( + filenameToRoute( + files.map((file) => { + const relativePath = relative(join(options.cwd || process.cwd(), 'pages'), file); + const path = transformFilename(removeExtension(relativePath), true); + + return { + filename: relativePath.replace(/\\/g, '/'), + regex: null, + params: null, + path, + id: performance.now(), + module: { + type: 'unknown', + isLoaded: false, + }, + }; + }) + ).map((file) => { + const outregex = pathToRegexp(file.path); + + return { + ...file, + regex: outregex.pattern, + params: outregex.params, + }; + }), + options + ); + + await pages.productionBuild(); +} diff --git a/packages/zely-js-core/src/server.ts b/packages/zely-js-core/src/server.ts index be761d29..2d3bd130 100644 --- a/packages/zely-js-core/src/server.ts +++ b/packages/zely-js-core/src/server.ts @@ -143,6 +143,10 @@ export async function createZelyServer(options: UserConfig) { options ); + if (process.env.NODE_ENV === 'production') { + await pages.productionBuild(); + } + const applyZelyMiddlewares = (serverInstance: ZeptServer) => { // Request/Response => ZelyRequest/Response serverInstance.use(kitMiddleware); diff --git a/packages/zely-js-core/types/config.d.ts b/packages/zely-js-core/types/config.d.ts index f6f588b2..ab0e6ff3 100644 --- a/packages/zely-js-core/types/config.d.ts +++ b/packages/zely-js-core/types/config.d.ts @@ -43,4 +43,6 @@ export interface UserConfig { * @default true */ enableReporter?: boolean; + + keepDist?: boolean; } diff --git a/packages/zely-js-core/types/functions.d.ts b/packages/zely-js-core/types/functions.d.ts index de95be01..0edaf91c 100644 --- a/packages/zely-js-core/types/functions.d.ts +++ b/packages/zely-js-core/types/functions.d.ts @@ -1,9 +1,8 @@ import { ZeptServer } from '@zept/http'; import { UserConfig } from './config'; -export function createZelyServer( - options: UserConfig -): Promise<{ +export function createZelyServer(options: UserConfig): Promise<{ server: ZeptServer; applyZelyMiddlewares: (serverInstance: ZeptServer) => void; }>; +export function productionBuild(options: UserConfig): Promise; diff --git a/packages/zely-reporter/index.js b/packages/zely-reporter/index.js index d251075c..5819ebda 100644 --- a/packages/zely-reporter/index.js +++ b/packages/zely-reporter/index.js @@ -6,7 +6,7 @@ const { dirname, join, relative } = require('path'); const errorHandler = async (e) => { if (!e) return; if (process.env.NODE_ENV === 'production') { - throw new Error(e); + console.error(e); } const stacks = parseError(e); diff --git a/playground/typescript/package.json b/playground/typescript/package.json index 3a337667..5339410d 100644 --- a/playground/typescript/package.json +++ b/playground/typescript/package.json @@ -8,6 +8,7 @@ "zely-cli": "workspace:*" }, "scripts": { + "start": "zely-cli start", "dev": "zely-cli dev" } } diff --git a/playground/typescript/zely.config.ts b/playground/typescript/zely.config.ts index 822bcac9..b0718841 100644 --- a/playground/typescript/zely.config.ts +++ b/playground/typescript/zely.config.ts @@ -2,10 +2,6 @@ import { defineConfig } from '@zely-js/zely'; export default defineConfig({ allowAutoMiddlewares: true, - onError(err) { - console.log('ERROR' + err); - }, - enableReporter: false, plugins: [ { name: 'error',