瀏覽代碼

chore: refactor node + cli

userquin 2 年之前
父節點
當前提交
906ec6710e

+ 3 - 0
plugins/vite-plugin-tres/bin/tres.mjs

@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+'use strict'
+import('../dist/node/cli.js')

+ 27 - 0
plugins/vite-plugin-tres/build.config.ts

@@ -0,0 +1,27 @@
+import { defineBuildConfig } from 'unbuild'
+
+import pkg from './package.json' assert { type: 'json' }
+
+const externals = [
+  ...Object.keys(pkg.dependencies),
+  ...Object.keys(pkg.peerDependencies || {}),
+  'vite',
+  'fs',
+  'events',
+  'pathe',
+  'fast-glob',
+]
+
+export default defineBuildConfig({
+  entries: [{ input: 'src/node/cli', name: 'cli' }],
+  clean: true,
+  declaration: false,
+  externals,
+  rollup: {
+    alias: {
+      entries: [{ find: /^node:(.+)$/, replacement: '$1' }],
+    },
+    emitCJS: false,
+    inlineDependencies: true,
+  },
+})

+ 0 - 0
plugins/vite-plugin-tres/pnpm-workspace.yaml


+ 52 - 0
plugins/vite-plugin-tres/src/node/cli-start.ts

@@ -0,0 +1,52 @@
+import cac from 'cac'
+import { consola } from 'consola'
+import { green } from 'colorette'
+import { createServer } from 'vite'
+import { VERSION } from './constants'
+import type { TresOptions, ResolvedTresOptions } from './config'
+import { loadConfig } from './config'
+import { TresPlugin } from './plugin'
+
+export async function startCli(args: string[] = process.argv) {
+  const cli = cac('tres-ci')
+
+  cli
+    .version(VERSION)
+    .option('-r, --root <path>', 'Root path')
+    .option('-c, --config <path>', 'Path to config file')
+    .help()
+    .command('[...files]', 'Run Tres CI')
+    .action((files, options) => run(files, options))
+
+  cli.parse(args)
+}
+
+async function run(_files: string[] = [], userConfig: TresOptions = {}) {
+  consola.log(green(`Tres CI v${VERSION}`))
+  consola.start('Starting Vite server...')
+
+  const root = userConfig?.root ?? process.cwd()
+
+  const { config } = await loadConfig<TresOptions>(root, userConfig)
+
+  config.root = root
+
+  config.plugins = config.plugins ?? []
+
+  const { entryPoint, ...vite } = config
+
+  const resolvedConfig: ResolvedTresOptions = {
+    vite,
+    entryPoint,
+  }
+
+  config.plugins.unshift(TresPlugin(true, resolvedConfig))
+
+  const server = await createServer(vite)
+
+  await server.pluginContainer.buildStart({})
+
+  await server.listen()
+
+  consola.ready('Tres CLI is ready: ', config.server?.port ?? 5173)
+}

+ 4 - 0
plugins/vite-plugin-tres/src/node/cli.ts

@@ -0,0 +1,4 @@
+import { startCli } from './cli-start'
+import { handleError } from './errors'
+
+startCli().catch(handleError)

+ 83 - 2
plugins/vite-plugin-tres/src/node/config.ts

@@ -1,3 +1,84 @@
-export interface Options {
+import { UserConfig } from 'vite'
+import { dirname, resolve } from 'node:path'
+import { existsSync, statSync } from 'node:fs'
+import { LoadConfigResult, LoadConfigSource } from 'unconfig'
+import { createConfigLoader as createLoader } from 'unconfig'
 
-}
+export interface TresOptions extends UserConfig {
+  /**
+   * Path to the config file.
+   *
+   * Default resolving to `tres.config.[js|mjs|cjs|ts|cts|mts]`
+   *
+   * Setting to `false` will disable config resolving.
+   */
+  config?: string | false
+  /**
+   * @default 'index.html'
+   */
+  entryPoint?: string
+}
+
+export interface ResolvedTresOptions {
+  entryPoint: string
+  vite: UserConfig
+}
+
+const defaultConfig: TresOptions = {
+  base: '/',
+  server: {
+    open: true,
+  },
+  entryPoint: 'index.html',
+}
+
+export async function loadConfig<U extends TresOptions>(
+  cwd = process.cwd(),
+  configOrPath: string | U = cwd,
+  extraConfigSources: LoadConfigSource[] = [],
+  defaults: TresOptions = defaultConfig,
+): Promise<LoadConfigResult<U>> {
+  let inlineConfig = {} as U
+  if (typeof configOrPath !== 'string') {
+    inlineConfig = configOrPath
+    if (inlineConfig.config === false) {
+      return {
+        config: inlineConfig as U,
+        sources: [],
+      }
+    } else {
+      configOrPath = inlineConfig.config || process.cwd()
+    }
+  }
+
+  const resolved = resolve(cwd, configOrPath)
+
+  let isFile = false
+  if (existsSync(resolved) && statSync(resolved).isFile()) {
+    isFile = true
+    cwd = dirname(resolved).replace(/\\/g, '/')
+  }
+
+  const loader = createLoader<U>({
+    sources: isFile
+      ? [
+          {
+            files: resolved,
+            extensions: [],
+          },
+        ]
+      : [
+          {
+            files: ['tres.config'],
+          },
+          ...extraConfigSources,
+        ],
+    cwd,
+    defaults: inlineConfig,
+  })
+
+  const result = await loader.load()
+  result.config = Object.assign(defaults, result.config || inlineConfig)
+
+  return result
+}

+ 5 - 0
plugins/vite-plugin-tres/src/node/constants.ts

@@ -0,0 +1,5 @@
+import { readFileSync } from 'node:fs'
+
+const { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url)).toString())
+
+export const VERSION = version as string

+ 21 - 0
plugins/vite-plugin-tres/src/node/errors.ts

@@ -0,0 +1,21 @@
+import { consola } from 'consola'
+
+export class PrettyError extends Error {
+  constructor(message: string) {
+    super(message)
+    this.name = this.constructor.name
+
+    if (typeof Error.captureStackTrace === 'function')
+      Error.captureStackTrace(this, this.constructor)
+
+    else
+      this.stack = new Error(message).stack
+  }
+}
+
+export function handleError(error: unknown) {
+  if (error instanceof PrettyError)
+    consola.error(error.message)
+
+  process.exitCode = 1
+}

+ 0 - 104
plugins/vite-plugin-tres/src/node/index.ts

@@ -1,104 +0,0 @@
-import { UserConfig, type Connect, type Plugin, type ResolvedConfig, type ViteDevServer } from 'vite'
-import sirv from 'sirv'
-import { createServer } from 'node:http'
-import type { AddressInfo } from 'node:net'
-import { isAbsolute, join, resolve } from 'node:path'
-import fs from 'fs-extra'
-import { Options } from './config'
-import { lightGreen, yellow, gray, bold } from 'kolorist'
-
-import { DIR_CLIENT } from '../dir'
-
-export const plugin = (useSirv = false, options: Options = {}) => {
-  const outputDir = options.outputDir ?? '.tres-inspect'
-  /* let config: ResolvedConfig */
-
-  async function generateBuild() {
-    const targetDir = /* isAbsolute(outputDir)
-      ? outputDir
-      : resolve(config.root, outputDir) */
-      outputDir
-
-    await fs.emptyDir(targetDir)
-    await fs.copy(DIR_CLIENT, targetDir)
-
-    await fs.writeFile(
-      join(targetDir, 'index.html'),
-      (await fs.readFile(join(targetDir, 'index.html'), 'utf-8'))
-        .replace(
-          'data-tres-inspect-mode="DEV"',
-          'data-tres-inspect-mode="BUILD"',
-        ),
-    )
-
-    return targetDir
-  }
-
-  function configureServer(server: ViteDevServer) {
-    const base = (options.base ?? server.config.base) || '/'
-
-    server.middlewares.use((req, res, next) => {
-      console.log(req.url)
-      next()
-    })
-
-    if (useSirv) {
-      server.middlewares.use(`${base}__tres`, sirv('.', {
-        single: true,
-        dev: true,
-      }))
-    }
-  }
-
-  function createPreviewServer(staticPath: string) {
-    const server = createServer()
-
-    const statics = sirv(staticPath)
-    server.on('request', (req, res) => {
-      statics(req, res, () => {
-        res.statusCode = 404
-        res.end('File not found')
-      })
-    })
-
-    server.listen(0, () => {
-      const { port } = server.address() as AddressInfo
-      const url = `http://localhost:${port}`
-      // eslint-disable-next-line no-console
-      console.log(`  ${lightGreen('➜')}  ${bold('Tres Inspect Preview Started')}: ${url}`)
-      openBrowser(url)
-    })
-
-    async function openBrowser(address: string) {
-      await import('open')
-        .then(r => r.default(address, { newInstance: true }))
-        .catch(() => { })
-    }
-  }
-
-  return {
-    name: 'vite-plugin-tres',
-    enforce: 'pre',
-    apply: 'serve',
-    config(config) {
-      const thisConfig: UserConfig = {}
-      if (typeof config.server?.open === 'undefined') {
-        thisConfig.server = {
-          open: true,
-        }
-      }
-      console.log(thisConfig)
-      return thisConfig
-    },
-    configureServer,
-    /*     configureServer, */
-    /*  async buildStart() {
-       const dir = await generateBuild()
-       console.log(`  ${lightGreen('➜')}  ${bold('Tres Inspect Build Completed')}: ${dir}`)
-       createPreviewServer(dir)
-     } */
-  }
-
-}
-
-export default plugin

+ 80 - 0
plugins/vite-plugin-tres/src/node/plugin.ts

@@ -0,0 +1,80 @@
+import { type UserConfig, type Plugin, ViteDevServer } from 'vite'
+import sirv from 'sirv'
+import { resolve } from 'pathe'
+import type { ResolvedTresOptions, TresOptions } from './config'
+import { readFile } from 'node:fs/promises'
+import { fileURLToPath } from 'node:url'
+
+export function TresPlugin(useSirv = false, options: ResolvedTresOptions = {}) {
+  return <Plugin>{
+    name: 'vite-plugin-tres',
+    enforce: 'pre',
+    apply: 'serve',
+    config() {
+      return options.vite
+    },
+    configureServer(server: ViteDevServer) {
+      const projectRoot = server.config.root ?? process.cwd()
+
+      let base = server.config.base
+      if (!base.endsWith('/')) {
+        base += '/'
+      }
+
+      const entryPoint = `${base}${options.entryPoint}`
+      const tresCITarget = `${base}__tres__ci__.html`
+      const tresCIEntryPoint = `${base}__tres__ci__target__.html`
+
+      const tresHtmlCIFile = resolve(fileURLToPath(import.meta.url), '../../client/tres-ci.html')
+      const tresHtmlFile = resolve(fileURLToPath(import.meta.url), '../../client/tres.html')
+      const tresTargetHtmlFile = resolve(projectRoot, options.entryPoint ?? 'index.html')
+
+      const tresHtmlCIPromise = readFile(tresHtmlCIFile, 'utf-8')
+      const tresHtmlPromise = readFile(tresHtmlFile, 'utf-8')
+      const tresTargetHtmlPromise = readFile(tresTargetHtmlFile, 'utf-8')
+
+      server.middlewares.use(async (req, res, next) => {
+        const url = req.url ?? ''
+
+        // intercepting entry point: `${base}` or `${base}${options.entryPoint ?? 'index.html'}`
+        if (url === base || url === entryPoint) {
+          console.log(url, await tresHtmlCIPromise)
+          res.setHeader('Content-Type', 'text/html')
+          res.statusCode = 200
+          res.end(await tresHtmlCIPromise)
+          return
+        }
+
+        // intercepting CI entry point: `${base}__tres__ci__/`
+        if (url === tresCITarget) {
+          console.log(url, await tresHtmlPromise)
+          res.setHeader('Content-Type', 'text/html')
+          res.statusCode = 200
+          res.end(await tresHtmlPromise)
+          return
+        }
+
+        // intercepting CI target: `${base}__tres_ci_target__.html`
+        if (url === tresCIEntryPoint) {
+          console.log(url, await tresTargetHtmlPromise)
+          res.setHeader('Content-Type', 'text/html')
+          res.statusCode = 200
+          res.end(await tresTargetHtmlPromise)
+          return
+        }
+
+        next()
+      })
+
+      if (useSirv) {
+        server.middlewares.use(
+          `${base}__tres_ci__`,
+          sirv(resolve(tresHtmlFile, '..'), {
+            single: true,
+            dev: true,
+          }),
+        )
+      }
+    },
+  }
+}

文件差異過大導致無法顯示
+ 486 - 40
pnpm-lock.yaml


+ 1 - 0
pnpm-workspace.yaml

@@ -1,3 +1,4 @@
 packages:
   - docs
   - playground
+  - 'plugins/*'

部分文件因文件數量過多而無法顯示