From 725a2a1f659b078973c2021d2bc5686db6887eb7 Mon Sep 17 00:00:00 2001 From: Valerio Ageno Date: Sat, 27 Apr 2024 14:00:17 +0200 Subject: [PATCH] feat: created base vite-fs-router plugin --- .gitignore | 3 + eslintrc.cjs | 133 ++++++++++++------------ examples/tuono/README.md | 1 + package.json | 5 +- packages/README.md | 3 + packages/vite-fs-router/package.json | 38 ++++++- packages/vite-fs-router/src/compiler.ts | 7 ++ packages/vite-fs-router/src/index.ts | 96 +++++++++++++++++ packages/vite-fs-router/vite.config.ts | 75 +++++++++++++ pnpm-workspace.yaml | 1 + 10 files changed, 289 insertions(+), 73 deletions(-) create mode 100644 examples/tuono/README.md create mode 100644 packages/README.md create mode 100644 packages/vite-fs-router/src/compiler.ts create mode 100644 packages/vite-fs-router/vite.config.ts diff --git a/.gitignore b/.gitignore index b8e88be6..87dcb2fe 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ dist/ .turbo/ pnpm-lock.yaml + +examples/playground/ + diff --git a/eslintrc.cjs b/eslintrc.cjs index 8d3f47a3..54629a03 100644 --- a/eslintrc.cjs +++ b/eslintrc.cjs @@ -4,16 +4,16 @@ const config = { root: true, reportUnusedDisableDirectives: true, - ignorePatterns: ["**/build", "**/dist"], - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "import"], + ignorePatterns: ['**/build', '**/dist'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'import'], extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/stylistic", - "plugin:import/recommended", - "plugin:import/typescript", - "prettier", + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/stylistic', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'prettier', ], env: { browser: true, @@ -22,86 +22,87 @@ const config = { parserOptions: { tsconfigRootDir: __dirname, project: true, - sourceType: "module", + sourceType: 'module', ecmaVersion: 2020, }, settings: { - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"], + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], }, - "import/resolver": { + 'import/resolver': { typescript: true, }, react: { - version: "detect", + version: 'detect', }, }, rules: { - "@typescript-eslint/array-type": "error", - "@typescript-eslint/ban-types": "error", - "@typescript-eslint/ban-ts-comment": "error", - "@typescript-eslint/consistent-type-definitions": "error", - "@typescript-eslint/consistent-type-imports": [ - "error", - { prefer: "type-imports" }, + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/ban-types': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports' }, ], - "@typescript-eslint/explicit-module-boundary-types": "error", - "@typescript-eslint/method-signature-style": ["error", "property"], - "@typescript-eslint/naming-convention": [ - "error", + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/method-signature-style': ['error', 'property'], + '@typescript-eslint/naming-convention': [ + 'error', { - selector: "typeParameter", - format: ["PascalCase"], - leadingUnderscore: "forbid", - trailingUnderscore: "forbid", + selector: 'typeParameter', + format: ['PascalCase'], + leadingUnderscore: 'forbid', + trailingUnderscore: 'forbid', custom: { - regex: "^(T|T[A-Z][A-Za-z]+)$", + regex: '^(T|T[A-Z][A-Za-z]+)$', match: true, }, }, ], - "@typescript-eslint/no-empty-function": "error", - "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-non-null-assertion": "error", - "@typescript-eslint/no-unnecessary-condition": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-unused-vars": "error", - "@typescript-eslint/no-inferrable-types": [ - "error", + '@typescript-eslint/no-empty-function': 'error', + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-unnecessary-condition': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/no-inferrable-types': [ + 'error', { ignoreParameters: true }, ], - "import/default": "error", - "import/export": "error", - "import/namespace": "error", - "import/newline-after-import": "error", - "import/no-cycle": "error", - "import/no-duplicates": "off", - "import/no-named-as-default-member": "error", - "import/no-unused-modules": "error", - "import/order": [ - "error", + 'import/default': 'error', + 'import/export': 'error', + 'import/namespace': 'error', + 'import/newline-after-import': 'error', + 'import/no-cycle': 'error', + 'import/no-duplicates': 'off', + 'import/no-named-as-default-member': 'error', + 'import/no-unused-modules': 'error', + 'import/order': [ + 'error', { groups: [ - "builtin", - "external", - "internal", - "parent", - "sibling", - "index", - "object", - "type", + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'type', ], }, ], - "no-case-declarations": "error", - "no-empty": "error", - "no-prototype-builtins": "error", - "no-redeclare": "error", - "no-shadow": "error", - "no-undef": "error", - "sort-imports": "error", + 'no-case-declarations': 'error', + 'no-empty': 'error', + 'no-prototype-builtins': 'error', + 'no-redeclare': 'error', + 'no-shadow': 'error', + 'no-undef': 'error', + 'sort-imports': 'error', }, -}; +} -module.exports = config; +module.exports = config diff --git a/examples/tuono/README.md b/examples/tuono/README.md new file mode 100644 index 00000000..f93367c3 --- /dev/null +++ b/examples/tuono/README.md @@ -0,0 +1 @@ +# TODO: Basic Tuono app diff --git a/package.json b/package.json index 5e44c06e..0fe5ce00 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,14 @@ "main": "index.js", "packageManager": "pnpm@8.12.1", "scripts": { - "lint": "turbo lint", - "format": "turbo format" + "lint": "turbo lint", + "format": "turbo format" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { + "@types/node": "^20.12.7", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 00000000..177623bc --- /dev/null +++ b/packages/README.md @@ -0,0 +1,3 @@ +# Tuono packages + +- `vite-fs-router`: Handles the bundling diff --git a/packages/vite-fs-router/package.json b/packages/vite-fs-router/package.json index d1324cc6..ea825689 100644 --- a/packages/vite-fs-router/package.json +++ b/packages/vite-fs-router/package.json @@ -1,13 +1,41 @@ { - "name": "vite-fs-router", + "name": "vite-plugin-fs-router", "version": "0.1.0", "description": "", - "main": "src/index.js", + "main": "src/index.ts", "scripts": { - "lint": "eslint --ext .ts,.tsx ./src -c ../../eslintrc.cjs", - "format": "prettier --ignore-unknown '**/*'" + "dev": "vite dev", + "build": "vite build", + "lint": "eslint --ext .ts,.tsx ./src -c ../../eslintrc.cjs", + "format": "prettier --ignore-unknown '**/*'", + "types": "tsc --noEmit" }, + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "dist", + "src/**" + ], + "sideEffects": false, "keywords": [], + "type": "module", "author": "Valerio Ageno", - "license": "MIT" + "license": "MIT", + "devDependencies": { + "rollup-plugin-preserve-directives": "^0.4.0", + "vite": "^5.2.10", + "vite-plugin-dts": "^3.9.0", + "vite-plugin-externalize-deps": "^0.8.0" + } } diff --git a/packages/vite-fs-router/src/compiler.ts b/packages/vite-fs-router/src/compiler.ts new file mode 100644 index 00000000..cf7728f8 --- /dev/null +++ b/packages/vite-fs-router/src/compiler.ts @@ -0,0 +1,7 @@ +export const makeCompile = () => { + console.log('compiling [compile fn]') +} + +export const splitFile = async ({}) => { + console.log('Split file [splitFile fn]') +} diff --git a/packages/vite-fs-router/src/index.ts b/packages/vite-fs-router/src/index.ts index e69de29b..3a2243fc 100644 --- a/packages/vite-fs-router/src/index.ts +++ b/packages/vite-fs-router/src/index.ts @@ -0,0 +1,96 @@ +import { normalize } from 'path' +import { fileURLToPath, pathToFileURL } from 'url' +import type { Plugin } from 'vite' +import { makeCompile, splitFile } from './compiler' + +const SPLIT_PREFIX = 'sp-' +const ROUTES_DIRECTORY_PATH = 'src/routes' +const DEBUG = true + +let lock = false + +export function RouterGenerator(): Plugin { + const _generate = async () => { + if (lock) return + lock = true + + try { + // TODO: generator function + console.log('Generating [generate fn]') + } catch (err) { + console.log(err) + } finally { + lock = false + } + } + + const handleFile = async (file: string) => { + const filePath = normalize(file) + + if (filePath.startsWith(ROUTES_DIRECTORY_PATH)) { + // TODO: generator function + console.log('Generating [handleFile fn]') + } + } + + return { + name: 'vite-plugin-fs-router-generator', + watchChange: async (file: string, context: { event: string }) => { + if (['create', 'update', 'delete'].includes(context.event)) { + await handleFile(file) + } + }, + } +} + +export function RouterCodeSplitter(): Plugin { + return { + name: 'vite-plugin-fs-router-code-splitter', + enforce: 'pre', + resolveId(source) { + if (source.startsWith(SPLIT_PREFIX + ':')) { + return source.replace(SPLIT_PREFIX + ':', '') + } + return + }, + async transform(code, id) { + const url = pathToFileURL(id) + url.searchParams.delete('v') + id = fileURLToPath(url).replace(/\\/g, '/') + + const compile = makeCompile() + if (DEBUG) console.info('Route: ', id) + + if (id.includes(SPLIT_PREFIX)) { + if (DEBUG) console.info('Splitting route: ', id) + + const compiled = await splitFile({ + code, + compile, + filename: id, + }) + + if (DEBUG) { + console.info('') + console.info('Split Output') + console.info('') + //console.info(compiled.code) + console.info('') + console.info('') + console.info('') + console.info('') + console.info('') + console.info('') + console.info('') + console.info('') + } + + return compiled + } + }, + } +} + +export function ViteFsRouter(): Array { + return [RouterGenerator(), RouterCodeSplitter()] +} diff --git a/packages/vite-fs-router/vite.config.ts b/packages/vite-fs-router/vite.config.ts new file mode 100644 index 00000000..329e88ea --- /dev/null +++ b/packages/vite-fs-router/vite.config.ts @@ -0,0 +1,75 @@ +import { defineConfig } from 'vite' +import { preserveDirectives } from 'rollup-plugin-preserve-directives' +import { externalizeDeps } from 'vite-plugin-externalize-deps' +import dts from 'vite-plugin-dts' + +const config = ({ entryRoot, include, exclude }) => + defineConfig({ + plugins: [ + externalizeDeps(), + preserveDirectives(), + dts({ + outDir: 'dist/esm', + entryRoot, + include, + exclude, + compilerOptions: { + // @ts-expect-error + module: 'esnext', + declarationMap: false, + }, + beforeWriteFile: (filePath, content) => { + content = content.replace( + /^(im|ex)port\s[\w{}*\s,]+from\s['"]\.\/[^.'"]+(?=['"];?$)/gm, + '$&.js', + ) + + return { filePath, content } + }, + }), + dts({ + outDir: 'dist/cjs', + entryRoot, + include, + exclude, + compilerOptions: { + // @ts-expect-error + module: 'commonjs', + declarationMap: false, + }, + beforeWriteFile: (filePath, content) => { + content = content.replace( + /^(im|ex)port\s[\w{}*\s,]+from\s['"]\.\/[^.'"]+(?=['"];?$)/gm, + '$&.cjs', + ) + + filePath = filePath.replace('.d.ts', '.d.cts') + + return { filePath, content } + }, + }), + ], + build: { + outDir: 'dist', + minify: false, + sourcemap: true, + lib: { + entry: entryRoot, + formats: ['es', 'cjs'], + fileName: (format) => { + if (format === 'cjs') return 'cjs/[name].cjs' + return 'esm/[name].js' + }, + }, + rollupOptions: { + output: { + preserveModules: true, + }, + }, + }, + }) + +export default config({ + entryRoot: './src/index.ts', + include: './src', +}) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dee51e92..d15a1f0b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,3 @@ packages: - "packages/*" + - "!**/examples/**"