├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── README.md ├── apps ├── demo-typedef │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── soori.config.js │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.css │ │ ├── jsons │ │ │ ├── cat1.json │ │ │ ├── cat2.json │ │ │ ├── hello.json │ │ │ └── hey.json │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── demo-virtual-module │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── soori.config.js │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.css │ │ ├── jsons │ │ │ ├── cat1.json │ │ │ ├── cat2.json │ │ │ ├── hello.json │ │ │ └── hey.json │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── demo-vue-markdown │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── soori.config.js │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── vue.svg │ │ ├── main.js │ │ ├── md │ │ │ └── about.md │ │ └── style.css │ └── vite.config.js └── demo │ ├── .eslintrc.cjs │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ └── vite.svg │ ├── soori.config.js │ ├── src │ ├── assets │ │ └── react.svg │ ├── index.css │ ├── jsons │ │ ├── cat1.json │ │ ├── cat2.json │ │ ├── hello.json │ │ └── hey.json │ ├── main.tsx │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── package.json ├── packages ├── plugin-json │ ├── .gitignore │ ├── build.config.ts │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── fixture1 │ │ │ │ ├── test1.json │ │ │ │ └── test2.json │ │ │ ├── fixture2 │ │ │ │ ├── nested │ │ │ │ │ └── test3.json │ │ │ │ ├── test1.json │ │ │ │ └── test2.json │ │ │ └── index.test.ts │ │ ├── index.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ └── vite.config.ts └── soori │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── build.config.ts │ ├── cli.js │ ├── package.json │ ├── src │ ├── build │ │ ├── __tests__ │ │ │ └── runPlugins.test.ts │ │ ├── build.ts │ │ ├── config.ts │ │ ├── index.ts │ │ ├── output.ts │ │ └── runPlugins.ts │ ├── cli.ts │ ├── config.ts │ ├── index.ts │ ├── plugin.ts │ ├── test.ts │ ├── types │ │ └── index.ts │ ├── utils │ │ ├── index.ts │ │ └── log.ts │ ├── vite-env.d.ts │ └── vite.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── scripts └── release.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "printWidth": 80, 6 | "tabWidth": 2 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.0.5 5 | 6 | [compare changes](https://github.com/eunjae-lee/soori/compare/plugin-json@0.0.1...v0.0.5) 7 | 8 | ### 🚀 Enhancements 9 | 10 | - Rewrite from scratch ([004405c](https://github.com/eunjae-lee/soori/commit/004405c)) 11 | 12 | ### 📖 Documentation 13 | 14 | - Update the docs ([af087ee](https://github.com/eunjae-lee/soori/commit/af087ee)) 15 | - Update the docs ([7246863](https://github.com/eunjae-lee/soori/commit/7246863)) 16 | - Update the docs ([d02ac8f](https://github.com/eunjae-lee/soori/commit/d02ac8f)) 17 | - Update the docs ([b5954f1](https://github.com/eunjae-lee/soori/commit/b5954f1)) 18 | - Update the docs ([39704f4](https://github.com/eunjae-lee/soori/commit/39704f4)) 19 | - Add expressive-code ([bf2773d](https://github.com/eunjae-lee/soori/commit/bf2773d)) 20 | 21 | ### ❤️ Contributors 22 | 23 | - Eunjae Lee 24 | 25 | ## v0.0.4 26 | 27 | 28 | ### 🚀 Enhancements 29 | 30 | - Add basic implementation ([db1dbcb](https://github.com/eunjae-lee/soori/commit/db1dbcb)) 31 | - Support submodule ([864397d](https://github.com/eunjae-lee/soori/commit/864397d)) 32 | - Support watcher ([14a6440](https://github.com/eunjae-lee/soori/commit/14a6440)) 33 | - Add @soori/plugin-json ([d0e4333](https://github.com/eunjae-lee/soori/commit/d0e4333)) 34 | - Add helper method for testing ([3296df3](https://github.com/eunjae-lee/soori/commit/3296df3)) 35 | - Support outputDir ([553b4a2](https://github.com/eunjae-lee/soori/commit/553b4a2)) 36 | - Support output.onBuildStart and output.onBuildEnd ([97f14fb](https://github.com/eunjae-lee/soori/commit/97f14fb)) 37 | - Support output config ([5152222](https://github.com/eunjae-lee/soori/commit/5152222)) 38 | 39 | ### 🩹 Fixes 40 | 41 | - Add logs ([8c68ede](https://github.com/eunjae-lee/soori/commit/8c68ede)) 42 | - **demo:** Convert markdown to vue ([c119f41](https://github.com/eunjae-lee/soori/commit/c119f41)) 43 | - Split handle into handle and handleEach ([20d9ebf](https://github.com/eunjae-lee/soori/commit/20d9ebf)) 44 | - Fix demos ([1d39993](https://github.com/eunjae-lee/soori/commit/1d39993)) 45 | - Rename outputMode to dryOutput ([5b910a8](https://github.com/eunjae-lee/soori/commit/5b910a8)) 46 | - Bind dir to execaCommand ([aa2469a](https://github.com/eunjae-lee/soori/commit/aa2469a)) 47 | 48 | ### 📖 Documentation 49 | 50 | - Add README ([a7c6a28](https://github.com/eunjae-lee/soori/commit/a7c6a28)) 51 | - Update README ([3e9dc68](https://github.com/eunjae-lee/soori/commit/3e9dc68)) 52 | - Update README ([c0a89a1](https://github.com/eunjae-lee/soori/commit/c0a89a1)) 53 | - Update README ([98a3410](https://github.com/eunjae-lee/soori/commit/98a3410)) 54 | - Update README ([e4cb8fd](https://github.com/eunjae-lee/soori/commit/e4cb8fd)) 55 | - Add docs ([40891e8](https://github.com/eunjae-lee/soori/commit/40891e8)) 56 | 57 | ### 🏡 Chore 58 | 59 | - Initial commit ([68d5754](https://github.com/eunjae-lee/soori/commit/68d5754)) 60 | - Clean up ([dc2ca33](https://github.com/eunjae-lee/soori/commit/dc2ca33)) 61 | - Update version to 0.0.2 ([b40a42a](https://github.com/eunjae-lee/soori/commit/b40a42a)) 62 | - Apply prettier ([ab69492](https://github.com/eunjae-lee/soori/commit/ab69492)) 63 | - Add .prettierignore ([d4075e5](https://github.com/eunjae-lee/soori/commit/d4075e5)) 64 | - Split files and add test ([2cf070a](https://github.com/eunjae-lee/soori/commit/2cf070a)) 65 | - Use resolve.alias for development ([ac4265a](https://github.com/eunjae-lee/soori/commit/ac4265a)) 66 | - Test build output ([bb8b74e](https://github.com/eunjae-lee/soori/commit/bb8b74e)) 67 | - Release v0.0.3 ([a240d13](https://github.com/eunjae-lee/soori/commit/a240d13)) 68 | - Clean up vite config ([b912f24](https://github.com/eunjae-lee/soori/commit/b912f24)) 69 | - Use unbuild ([95d1c83](https://github.com/eunjae-lee/soori/commit/95d1c83)) 70 | - Add demo-virtual-module ([b320a0b](https://github.com/eunjae-lee/soori/commit/b320a0b)) 71 | - Add example for virtual mode ([a87546e](https://github.com/eunjae-lee/soori/commit/a87546e)) 72 | - Add type definition example ([9f641dd](https://github.com/eunjae-lee/soori/commit/9f641dd)) 73 | - Add virtual module demo ([bee1429](https://github.com/eunjae-lee/soori/commit/bee1429)) 74 | - Add release script ([565f066](https://github.com/eunjae-lee/soori/commit/565f066)) 75 | - Upgrade deps ([2a410dc](https://github.com/eunjae-lee/soori/commit/2a410dc)) 76 | - Delete plugin template ([43a6f13](https://github.com/eunjae-lee/soori/commit/43a6f13)) 77 | - Support ts with zx ([50c4ce0](https://github.com/eunjae-lee/soori/commit/50c4ce0)) 78 | - Update release script ([5f408ca](https://github.com/eunjae-lee/soori/commit/5f408ca)) 79 | 80 | ### ❤️ Contributors 81 | 82 | - Eunjae Lee 83 | 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🪄 Soori 2 | 3 | Build your own compile-time library. 4 | 5 | ## Getting Started 6 | 7 | Install Soori as a dependency. 8 | 9 | ```sh 10 | npm install soori 11 | 12 | pnpm install soori 13 | 14 | yarn add soori 15 | ``` 16 | 17 | Modify your build script: 18 | 19 | ```json 20 | "scripts": { 21 | "build": "soori build && " 22 | } 23 | ``` 24 | 25 | ### vite 26 | 27 | If you're using vite, you don’t have to run `soori build` manually. 28 | 29 | ```ts 30 | // vite.config.js 31 | import { defineConfig } from 'vite'; 32 | import { soori } from 'soori/vite'; 33 | 34 | export default defineConfig({ 35 | plugins: [soori()], 36 | }); 37 | ``` 38 | 39 | ## How it works 40 | 41 | This is an example config: 42 | 43 | ```js 44 | // soori.config.js 45 | import { defineSooriPlugin } from 'soori'; 46 | import fs from 'node:fs/promises'; 47 | 48 | export default { 49 | plugins: [ 50 | defineSooriPlugin({ 51 | name: 'my-json-loader', 52 | watch: ['src/data/*.json'], 53 | output: 'my-json', 54 | build: async ({ filePath, filenameWithoutExt }) => { 55 | const fileContent = (await fs.readFile(filePath)).toString(); 56 | return { 57 | id: filenameWithoutExt, 58 | content: `export const ${filenameWithoutExt} = ${fileContent};`, 59 | }; 60 | }, 61 | }), 62 | ], 63 | }; 64 | ``` 65 | 66 | Imagine you have the following files under `src/data/`: 67 | 68 | - abc.json 69 | - def.json 70 | 71 | Then you will be able to import them in your application: 72 | 73 | ```js 74 | import { abc, def } from 'soori/my-json'; 75 | 76 | console.log('# my jsons', { abc, def }); 77 | ``` 78 | 79 | ### How does it work? 80 | 81 | That `build` function runs on each of those files. In case of `abc.json`, the parameters to the `build` function will be: 82 | 83 | - `filePath`: `src/data/abc.json` 84 | - `filenameWithoutExt`: `abc` 85 | 86 | Soori uses the return of the `build` function to generate an output file with the filename being `${id}.ts`, and the content being `content`. It means this sample plugin generates the following files: 87 | 88 | - `node_modules/soori/submodules/my-json/abc.ts` 89 | - `node_modules/soori/submodules/my-json/def.ts` 90 | - `node_modules/soori/submodules/my-json/index.ts` 91 | 92 | And that `index.ts` is an entry file which is automatically generated by Soori. 93 | 94 | ## API References 95 | 96 | The API design is in progress, so there is no finalized documentation. However, you can check out [the type declarations](https://github.com/eunjae-lee/soori/blob/main/packages/soori/src/types/index.ts). 97 | -------------------------------------------------------------------------------- /apps/demo-typedef/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /apps/demo-typedef/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/demo-typedef/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /apps/demo-typedef/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/demo-typedef/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-typedef", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@soori/plugin-json": "*", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "soori": "*" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.15", 20 | "@types/react-dom": "^18.2.7", 21 | "@typescript-eslint/eslint-plugin": "^6.0.0", 22 | "@typescript-eslint/parser": "^6.0.0", 23 | "@vitejs/plugin-react": "^4.0.3", 24 | "eslint": "^8.45.0", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.3", 27 | "typescript": "^5.0.2", 28 | "vite": "^4.4.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/demo-typedef/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-typedef/soori.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, definePlugin } from 'soori'; 2 | import json from '@soori/plugin-json'; 3 | import fs from 'fs/promises'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | definePlugin({ 8 | ...json({ 9 | name: 'my-jsons', 10 | objectPerFile: true, 11 | watch: ['src/jsons/*.json'], 12 | }), 13 | name: 'my-jsons', 14 | output: { 15 | packageExports: { 16 | import: './submodules/my-jsons/index.js', 17 | types: './submodules/my-jsons/types/index.d.ts', 18 | }, 19 | onBuildEnd: async ({ dir, execaCommand }) => { 20 | await execaCommand( 21 | `npx -p typescript tsc *.ts --target es2020 --moduleResolution bundler`, 22 | { 23 | cwd: dir, 24 | shell: true, 25 | } 26 | ); 27 | await execaCommand( 28 | `npx -p typescript tsc *.ts --declaration --emitDeclarationOnly --target es2020 --moduleResolution bundler --outDir types`, 29 | { cwd: dir, shell: true } 30 | ); 31 | }, 32 | }, 33 | }), 34 | ], 35 | }); 36 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import reactLogo from './assets/react.svg' 3 | import viteLogo from '/vite.svg' 4 | import './App.css' 5 | 6 | function App() { 7 | const [count, setCount] = useState(0) 8 | 9 | return ( 10 | <> 11 |
12 | 13 | Vite logo 14 | 15 | 16 | React logo 17 | 18 |
19 |

Vite + React

20 |
21 | 24 |

25 | Edit src/App.tsx and save to test HMR 26 |

27 |
28 |

29 | Click on the Vite and React logos to learn more 30 |

31 | 32 | ) 33 | } 34 | 35 | export default App 36 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/jsons/cat1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sorok" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/jsons/cat2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shaka" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/jsons/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/jsons/hey.json: -------------------------------------------------------------------------------- 1 | { 2 | "hey": "yo" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | import './index.css'; 5 | import { cat1 } from 'soori/my-jsons'; 6 | console.log({ cat1 }); 7 | 8 | ReactDOM.createRoot(document.getElementById('root')!).render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /apps/demo-typedef/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/demo-typedef/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /apps/demo-typedef/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-typedef/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import { soori } from 'soori/vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), soori()], 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-virtual-module", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "build:soori": "soori build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@soori/plugin-json": "*", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "soori": "*" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.15", 21 | "@types/react-dom": "^18.2.7", 22 | "@typescript-eslint/eslint-plugin": "^6.0.0", 23 | "@typescript-eslint/parser": "^6.0.0", 24 | "@vitejs/plugin-react": "^4.0.3", 25 | "eslint": "^8.45.0", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.4.3", 28 | "typescript": "^5.0.2", 29 | "vite": "^4.4.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/soori.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, definePlugin } from 'soori'; 2 | import json from '@soori/plugin-json'; 3 | import fs from 'fs/promises'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | definePlugin({ 8 | ...json({ 9 | name: 'my-jsons', 10 | objectPerFile: true, 11 | watch: ['src/jsons/*.json'], 12 | }), 13 | name: 'my-virtual-module', 14 | output: { 15 | dir: 'node_modules/my-virtual-module', 16 | onBuildEnd: async ({ dir, execaCommand }) => { 17 | await fs.writeFile( 18 | `${dir}/package.json`, 19 | JSON.stringify( 20 | { 21 | name: 'my-virtual-module', 22 | private: true, 23 | type: 'module', 24 | main: 'dist/index.cjs', 25 | module: 'dist/index.mjs', 26 | types: 'dist/index.d.ts', 27 | exports: { 28 | '.': { 29 | import: './dist/index.mjs', 30 | require: './dist/index.cjs', 31 | types: './dist/index.d.ts', 32 | }, 33 | }, 34 | }, 35 | null, 36 | 2 37 | ) 38 | ); 39 | await execaCommand(`rm -rf src`); 40 | await execaCommand(`mkdir src`); 41 | await execaCommand(`cp *.ts src/`, { shell: true }); 42 | await execaCommand(`npx unbuild`, { 43 | shell: true, 44 | }); 45 | }, 46 | }, 47 | }), 48 | ], 49 | }); 50 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import reactLogo from './assets/react.svg' 3 | import viteLogo from '/vite.svg' 4 | import './App.css' 5 | 6 | function App() { 7 | const [count, setCount] = useState(0) 8 | 9 | return ( 10 | <> 11 |
12 | 13 | Vite logo 14 | 15 | 16 | React logo 17 | 18 |
19 |

Vite + React

20 |
21 | 24 |

25 | Edit src/App.tsx and save to test HMR 26 |

27 |
28 |

29 | Click on the Vite and React logos to learn more 30 |

31 | 32 | ) 33 | } 34 | 35 | export default App 36 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/jsons/cat1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sorok" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/jsons/cat2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shaka" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/jsons/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/jsons/hey.json: -------------------------------------------------------------------------------- 1 | { 2 | "hey": "yo" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | import './index.css'; 5 | import { cat1, cat2 } from 'my-virtual-module'; 6 | console.log({ cat1, cat2 }); 7 | 8 | ReactDOM.createRoot(document.getElementById('root')!).render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo-virtual-module/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import { soori } from 'soori/vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [soori(), react()], 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated 2 | src/markdownInVue 3 | 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | pnpm-debug.log* 12 | lerna-debug.log* 13 | 14 | node_modules 15 | dist 16 | dist-ssr 17 | *.local 18 | 19 | # Editor directories and files 20 | .vscode/* 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Vite 2 | 3 | This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-vue-markdown", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "vue": "^3.3.4", 13 | "soori": "*" 14 | }, 15 | "devDependencies": { 16 | "@vitejs/plugin-vue": "^4.2.3", 17 | "unplugin-vue-markdown": "0.24.2", 18 | "vite": "^4.4.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/soori.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, definePlugin } from 'soori'; 2 | import markdown from 'unplugin-vue-markdown/vite'; 3 | import path from 'node:path'; 4 | import fs from 'node:fs/promises'; 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | definePlugin({ 9 | name: 'markdownInVue', 10 | output: { dir: 'src/markdownInVue' }, 11 | build: [ 12 | { 13 | watch: ['src/md/*.md'], 14 | handle: async () => { 15 | const files = await fs.readdir('src/md'); 16 | return { 17 | fileName: 'index.ts', 18 | content: files 19 | .map((file) => { 20 | const fileNameWithoutExt = file.replace(/\.md$/, ''); 21 | const componentName = 22 | fileNameWithoutExt[0].toUpperCase() + 23 | fileNameWithoutExt.slice(1); 24 | return `export { default as ${componentName} } from './${fileNameWithoutExt}.vue';`; 25 | }) 26 | .join('\n'), 27 | }; 28 | }, 29 | }, 30 | { 31 | watch: ['src/md/*.md'], 32 | handleEach: async ({ fullPath, fileNameWithoutExt }) => { 33 | const raw = (await fs.readFile(fullPath)).toString(); 34 | const id = path.resolve(fullPath); 35 | const result = await markdown().transform(raw, id); 36 | return { 37 | fileName: `${fileNameWithoutExt}.vue`, 38 | content: result.code, 39 | }; 40 | }, 41 | }, 42 | ], 43 | }), 44 | ], 45 | }); 46 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/src/md/about.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | Hello, I'm Eunjae! 4 | 5 | This is written in markdown. 6 | 7 | - list item 1 8 | - list item 2 9 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | a { 27 | font-weight: 500; 28 | color: #646cff; 29 | text-decoration: inherit; 30 | } 31 | a:hover { 32 | color: #535bf2; 33 | } 34 | 35 | body { 36 | margin: 0; 37 | display: flex; 38 | place-items: center; 39 | min-width: 320px; 40 | min-height: 100vh; 41 | } 42 | 43 | h1 { 44 | font-size: 3.2em; 45 | line-height: 1.1; 46 | } 47 | 48 | button { 49 | border-radius: 8px; 50 | border: 1px solid transparent; 51 | padding: 0.6em 1.2em; 52 | font-size: 1em; 53 | font-weight: 500; 54 | font-family: inherit; 55 | background-color: #1a1a1a; 56 | cursor: pointer; 57 | transition: border-color 0.25s; 58 | } 59 | button:hover { 60 | border-color: #646cff; 61 | } 62 | button:focus, 63 | button:focus-visible { 64 | outline: 4px auto -webkit-focus-ring-color; 65 | } 66 | 67 | .card { 68 | padding: 2em; 69 | } 70 | 71 | #app { 72 | max-width: 1280px; 73 | margin: 0 auto; 74 | padding: 2rem; 75 | text-align: center; 76 | } 77 | 78 | @media (prefers-color-scheme: light) { 79 | :root { 80 | color: #213547; 81 | background-color: #ffffff; 82 | } 83 | a:hover { 84 | color: #747bff; 85 | } 86 | button { 87 | background-color: #f9f9f9; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /apps/demo-vue-markdown/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import { soori } from 'soori/vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue(), soori()], 8 | }); 9 | -------------------------------------------------------------------------------- /apps/demo/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /apps/demo/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /apps/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.1", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build:soori": "soori build", 9 | "build": "vite build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@soori/plugin-json": "workspace:*", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "soori": "workspace:*" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.15", 21 | "@types/react-dom": "^18.2.7", 22 | "@typescript-eslint/eslint-plugin": "^6.0.0", 23 | "@typescript-eslint/parser": "^6.0.0", 24 | "@vitejs/plugin-react": "^4.0.3", 25 | "eslint": "^8.45.0", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.4.3", 28 | "typescript": "5.1.6", 29 | "vite": "^4.4.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/demo/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo/soori.config.js: -------------------------------------------------------------------------------- 1 | import { defineSooriConfig } from 'soori'; 2 | import json from '@soori/plugin-json'; 3 | 4 | export default defineSooriConfig({ 5 | plugins: [ 6 | json({ 7 | output: 'json-gen', 8 | watch: ['src/jsons/*.json'], 9 | objectPerFile: true, 10 | }), 11 | ], 12 | }); 13 | -------------------------------------------------------------------------------- /apps/demo/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/demo/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /apps/demo/src/jsons/cat1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sorok" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo/src/jsons/cat2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Shaka" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo/src/jsons/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo/src/jsons/hey.json: -------------------------------------------------------------------------------- 1 | { 2 | "hey": "yo" 3 | } 4 | -------------------------------------------------------------------------------- /apps/demo/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { cat1, cat2 } from 'soori/json-gen'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root')!).render( 6 | 7 |
8 |

Try editing `apps/demo/src/jsons/cat1.json` or `cat2.json`.

9 |
{JSON.stringify({ cat1, cat2 }, null, 2)}
10 |
11 |
12 | ); 13 | -------------------------------------------------------------------------------- /apps/demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | /* Bundler mode */ 9 | "moduleResolution": "bundler", 10 | "allowImportingTsExtensions": true, 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "noEmit": true, 14 | "jsx": "react-jsx", 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "include": ["src"], 22 | "references": [ 23 | { 24 | "path": "./tsconfig.node.json" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /apps/demo/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import { soori } from 'soori/vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), soori()], 8 | }); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soori", 3 | "private": true, 4 | "version": "0.0.1", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "pnpm -F soori run test", 9 | "dev:demo": "pnpm -F demo run dev", 10 | "build:lib": "pnpm -F soori run build", 11 | "release": "./scripts/release.ts" 12 | }, 13 | "author": "Eunjae Lee", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "tsx": "3.12.8", 17 | "zx": "7.2.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/plugin-json/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/plugin-json/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild'; 2 | 3 | export default defineBuildConfig({ 4 | entries: ['./src/index.ts'], 5 | declaration: true, 6 | externals: ['vite', 'soori'], 7 | rollup: { 8 | emitCJS: true, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/plugin-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@soori/plugin-json", 3 | "version": "0.0.2", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "test": "vitest", 8 | "build": "tsc && unbuild", 9 | "preview": "vite preview" 10 | }, 11 | "main": "dist/index.cjs", 12 | "module": "dist/index.mjs", 13 | "types": "dist/index.d.ts", 14 | "exports": { 15 | ".": { 16 | "import": "./dist/index.mjs", 17 | "require": "./dist/index.cjs", 18 | "types": "./dist/index.d.ts" 19 | } 20 | }, 21 | "devDependencies": { 22 | "@types/glob": "8.1.0", 23 | "@types/node": "20.5.1", 24 | "soori": "^0.0.5", 25 | "typescript": "5.0.2", 26 | "unbuild": "2.0.0", 27 | "vite": "4.4.9", 28 | "vitest": "0.34.2" 29 | }, 30 | "dependencies": { 31 | "glob": "^10.3.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/fixture1/test1.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world1" 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/fixture1/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world2" 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/fixture2/nested/test3.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world3" 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/fixture2/test1.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world1" 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/fixture2/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world2" 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin-json/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { testPlugins } from 'soori'; 2 | import path from 'node:path'; 3 | import json from '..'; 4 | 5 | describe('json', () => { 6 | it('exports all from json files', async () => { 7 | const outputs = await testPlugins({ 8 | plugins: [ 9 | json({ 10 | output: 'test-plugin', 11 | watch: [path.resolve(__dirname, 'fixture1') + '/*.json'], 12 | }), 13 | ], 14 | }); 15 | expect(outputs).toMatchInlineSnapshot(` 16 | { 17 | "node_modules/soori/submodules/test-plugin/index.ts": "export * from './test2'; 18 | 19 | export * from './test1';", 20 | "node_modules/soori/submodules/test-plugin/test1.ts": "export const hello = \\"world1\\";", 21 | "node_modules/soori/submodules/test-plugin/test2.ts": "export const hello = \\"world2\\";", 22 | } 23 | `); 24 | }); 25 | 26 | it('exports as each module from json files', async () => { 27 | const outputs = await testPlugins({ 28 | plugins: [ 29 | json({ 30 | output: 'test-plugin', 31 | watch: [path.resolve(__dirname, 'fixture1') + '/*.json'], 32 | objectPerFile: true, 33 | }), 34 | ], 35 | }); 36 | const entry = outputs['node_modules/soori/submodules/test-plugin/index.ts']; 37 | expect(entry).toContain('export * as test1'); 38 | expect(entry).toContain('export * as test2'); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/plugin-json/src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineSooriPlugin } from 'soori'; 2 | import fs from 'node:fs/promises'; 3 | 4 | type Options = { 5 | output: string; 6 | watch: string[]; 7 | objectPerFile?: boolean; 8 | }; 9 | 10 | export default ({ output, watch, objectPerFile }: Options) => 11 | defineSooriPlugin({ 12 | name: '@soori/plugin-json', 13 | watch, 14 | output, 15 | build: async ({ filePath, filenameWithoutExt }) => { 16 | try { 17 | const json = JSON.parse((await fs.readFile(filePath)).toString()); 18 | return { 19 | id: filenameWithoutExt, 20 | content: Object.keys(json) 21 | .map((key) => `export const ${key} = ${JSON.stringify(json[key])};`) 22 | .join('\n\n'), 23 | }; 24 | } catch (err) { 25 | return; 26 | } 27 | }, 28 | entry: ({ filenamesWithoutExt }) => { 29 | if (objectPerFile) { 30 | return filenamesWithoutExt 31 | .map((filename) => `export * as ${filename} from './${filename}';`) 32 | .join('\n\n'); 33 | } else { 34 | return filenamesWithoutExt 35 | .map((filename) => `export * from './${filename}';`) 36 | .join('\n\n'); 37 | } 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /packages/plugin-json/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/plugin-json/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable" 10 | ], 11 | "skipLibCheck": true, 12 | /* Bundler mode */ 13 | "moduleResolution": "bundler", 14 | "allowImportingTsExtensions": true, 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "types": [ 24 | "vitest/globals" 25 | ] 26 | }, 27 | "include": [ 28 | "src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-json/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/soori/.gitignore: -------------------------------------------------------------------------------- 1 | submodules 2 | package.generated.json 3 | -------------------------------------------------------------------------------- /packages/soori/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v0.0.5 5 | 6 | [compare changes](https://github.com/eunjae-lee/soori/compare/plugin-json@0.0.1...v0.0.5) 7 | 8 | ### 🚀 Enhancements 9 | 10 | - Rewrite from scratch ([004405c](https://github.com/eunjae-lee/soori/commit/004405c)) 11 | 12 | ### 📖 Documentation 13 | 14 | - Update the docs ([af087ee](https://github.com/eunjae-lee/soori/commit/af087ee)) 15 | - Update the docs ([7246863](https://github.com/eunjae-lee/soori/commit/7246863)) 16 | - Update the docs ([d02ac8f](https://github.com/eunjae-lee/soori/commit/d02ac8f)) 17 | - Update the docs ([b5954f1](https://github.com/eunjae-lee/soori/commit/b5954f1)) 18 | - Update the docs ([39704f4](https://github.com/eunjae-lee/soori/commit/39704f4)) 19 | - Add expressive-code ([bf2773d](https://github.com/eunjae-lee/soori/commit/bf2773d)) 20 | 21 | ### ❤️ Contributors 22 | 23 | - Eunjae Lee 24 | 25 | ## v0.0.4 26 | 27 | 28 | ### 🚀 Enhancements 29 | 30 | - Add basic implementation ([db1dbcb](https://github.com/eunjae-lee/soori/commit/db1dbcb)) 31 | - Support submodule ([864397d](https://github.com/eunjae-lee/soori/commit/864397d)) 32 | - Support watcher ([14a6440](https://github.com/eunjae-lee/soori/commit/14a6440)) 33 | - Add @soori/plugin-json ([d0e4333](https://github.com/eunjae-lee/soori/commit/d0e4333)) 34 | - Add helper method for testing ([3296df3](https://github.com/eunjae-lee/soori/commit/3296df3)) 35 | - Support outputDir ([553b4a2](https://github.com/eunjae-lee/soori/commit/553b4a2)) 36 | - Support output.onBuildStart and output.onBuildEnd ([97f14fb](https://github.com/eunjae-lee/soori/commit/97f14fb)) 37 | - Support output config ([5152222](https://github.com/eunjae-lee/soori/commit/5152222)) 38 | 39 | ### 🩹 Fixes 40 | 41 | - Add logs ([8c68ede](https://github.com/eunjae-lee/soori/commit/8c68ede)) 42 | - **demo:** Convert markdown to vue ([c119f41](https://github.com/eunjae-lee/soori/commit/c119f41)) 43 | - Split handle into handle and handleEach ([20d9ebf](https://github.com/eunjae-lee/soori/commit/20d9ebf)) 44 | - Fix demos ([1d39993](https://github.com/eunjae-lee/soori/commit/1d39993)) 45 | - Rename outputMode to dryOutput ([5b910a8](https://github.com/eunjae-lee/soori/commit/5b910a8)) 46 | - Bind dir to execaCommand ([aa2469a](https://github.com/eunjae-lee/soori/commit/aa2469a)) 47 | 48 | ### 📖 Documentation 49 | 50 | - Add README ([a7c6a28](https://github.com/eunjae-lee/soori/commit/a7c6a28)) 51 | - Update README ([3e9dc68](https://github.com/eunjae-lee/soori/commit/3e9dc68)) 52 | - Update README ([c0a89a1](https://github.com/eunjae-lee/soori/commit/c0a89a1)) 53 | - Update README ([98a3410](https://github.com/eunjae-lee/soori/commit/98a3410)) 54 | - Update README ([e4cb8fd](https://github.com/eunjae-lee/soori/commit/e4cb8fd)) 55 | - Add docs ([40891e8](https://github.com/eunjae-lee/soori/commit/40891e8)) 56 | 57 | ### 🏡 Chore 58 | 59 | - Initial commit ([68d5754](https://github.com/eunjae-lee/soori/commit/68d5754)) 60 | - Clean up ([dc2ca33](https://github.com/eunjae-lee/soori/commit/dc2ca33)) 61 | - Update version to 0.0.2 ([b40a42a](https://github.com/eunjae-lee/soori/commit/b40a42a)) 62 | - Apply prettier ([ab69492](https://github.com/eunjae-lee/soori/commit/ab69492)) 63 | - Add .prettierignore ([d4075e5](https://github.com/eunjae-lee/soori/commit/d4075e5)) 64 | - Split files and add test ([2cf070a](https://github.com/eunjae-lee/soori/commit/2cf070a)) 65 | - Use resolve.alias for development ([ac4265a](https://github.com/eunjae-lee/soori/commit/ac4265a)) 66 | - Test build output ([bb8b74e](https://github.com/eunjae-lee/soori/commit/bb8b74e)) 67 | - Release v0.0.3 ([a240d13](https://github.com/eunjae-lee/soori/commit/a240d13)) 68 | - Clean up vite config ([b912f24](https://github.com/eunjae-lee/soori/commit/b912f24)) 69 | - Use unbuild ([95d1c83](https://github.com/eunjae-lee/soori/commit/95d1c83)) 70 | - Add demo-virtual-module ([b320a0b](https://github.com/eunjae-lee/soori/commit/b320a0b)) 71 | - Add example for virtual mode ([a87546e](https://github.com/eunjae-lee/soori/commit/a87546e)) 72 | - Add type definition example ([9f641dd](https://github.com/eunjae-lee/soori/commit/9f641dd)) 73 | - Add virtual module demo ([bee1429](https://github.com/eunjae-lee/soori/commit/bee1429)) 74 | - Add release script ([565f066](https://github.com/eunjae-lee/soori/commit/565f066)) 75 | - Upgrade deps ([2a410dc](https://github.com/eunjae-lee/soori/commit/2a410dc)) 76 | - Delete plugin template ([43a6f13](https://github.com/eunjae-lee/soori/commit/43a6f13)) 77 | - Support ts with zx ([50c4ce0](https://github.com/eunjae-lee/soori/commit/50c4ce0)) 78 | - Update release script ([5f408ca](https://github.com/eunjae-lee/soori/commit/5f408ca)) 79 | 80 | ### ❤️ Contributors 81 | 82 | - Eunjae Lee 83 | 84 | -------------------------------------------------------------------------------- /packages/soori/README.md: -------------------------------------------------------------------------------- 1 | # 🪄 Soori 2 | 3 | Soori allows you to build compile-time libraries. 4 | 5 | (copy copied from 6 | [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros)) 7 | 8 | ## Problem 9 | 10 | ``` 11 | pages/ 12 | ㄴapi/ 13 | ㄴfunction1.js 14 | ㄴfunction2.js 15 | ㄴfunction3.js 16 | ``` 17 | 18 | **A quick quiz**: If your repository looks like the example above, how can you 19 | create an array containing the names of those files? 20 | 21 | ```js 22 | const functions = ['function1.js', 'function2.js', 'function3.js']; // <- How? 23 | ``` 24 | 25 | Off the top of your head, you might think about using `fs.readdir()` but once 26 | it's bundled and deployed, you won't be able to do that. It's better if you can 27 | handle it before bundling and deploying starts, during compile-time. 28 | 29 | What if Soori gives you something like this? 30 | 31 | ```js 32 | import functions from 'soori/my-functions'; 33 | 34 | console.log(functions); // ['function1.js', 'function2.js', 'function3.js'] 35 | ``` 36 | 37 | ## But, why? 38 | 39 | Okay, let's imagine: 40 | 41 | - Your website needs to fetch a big chunk of data from an API and renders it. 42 | - But you don't need to do it at runtime. 43 | - It's okay to fetch it once at compile-time. 44 | 45 | How would you implement this? You can write a Soori config that provides you 46 | `soori/something` module at compile-time. 47 | 48 | Or, what if you could write something similar to [tRPC](https://trpc.io/) on 49 | your own? 50 | 51 | ``` 52 | pages/ 53 | ㄴapi/ 54 | ㄴfunction1.js 55 | ㄴfunction2.js 56 | ㄴfunction3.js 57 | ``` 58 | 59 | No need to repeat this: 60 | 61 | ```js 62 | try { 63 | const response = await fetch('/api/function1?hello=world'); 64 | const json = await response.json(); 65 | } catch (e) { 66 | // deal with it 67 | } 68 | ``` 69 | 70 | But import methods like this: 71 | 72 | ```js 73 | import { function1 } from 'soori/my-nextjs-apis'; 74 | 75 | await function1({ hello: 'world' }); 76 | ``` 77 | 78 | > "Wait, what's the difference between writing a helper method that wraps a 79 | > fetch request and handles errors?" 80 | 81 | You're almost there. But you would have to do something like this: 82 | 83 | ```js 84 | // src/server/index.js 85 | 86 | const requester = (...) => { ... } 87 | 88 | export const function1 = requester({ path: '/api/function1', method: 'GET' }) 89 | export const function2 = requester({ path: '/api/function2', method: 'GET' }) 90 | export const function3 = ... 91 | ``` 92 | 93 | And, each time you add an additional API endpoint, you must perform this task 94 | manually. 95 | 96 | ## Is it production-ready? 97 | 98 | This is still a proof of concept. Never use it for production. However, the 99 | beauty of Soori is that it offers submodules during compile-time. If there are 100 | any issues, they will arise during compile-time. You can also view the generated 101 | code under `node_modules/soori/submodules/`. 102 | 103 | ## Configuration 104 | 105 | There is no proper documentation available as things are still unstable. 106 | Instead, please refer to this 107 | [this demo configuration](https://github.com/eunjae-lee/soori/blob/main/apps/demo/soori.config.js) 108 | and 109 | [its usage](https://github.com/eunjae-lee/soori/blob/main/apps/demo/src/main.tsx). 110 | 111 | ## How to generate submodules based on the config? 112 | 113 | You can run `soori build`. 114 | 115 | If you're using Vite, you can utilize Soori's Vite plugin for live watching and 116 | on-the-fly building for dev mode. 117 | 118 | ```js 119 | import { vite } from 'soori'; 120 | 121 | export default defineConfig({ 122 | plugins: [vite()], 123 | }); 124 | ``` 125 | -------------------------------------------------------------------------------- /packages/soori/build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig } from 'unbuild'; 2 | 3 | export default defineBuildConfig({ 4 | entries: ['./src/index.ts', './src/cli.ts', './src/vite.ts'], 5 | declaration: true, 6 | externals: ['chalk', 'commander', 'glob', 'minimatch', 'vite', 'execa'], 7 | rollup: { 8 | emitCJS: true, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/soori/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { main } from './dist/cli.mjs'; 4 | main(); 5 | -------------------------------------------------------------------------------- /packages/soori/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soori", 3 | "description": "Soori allows you to build compile-time libraries.", 4 | "version": "0.0.5", 5 | "type": "module", 6 | "bin": { 7 | "soori": "./cli.js" 8 | }, 9 | "files": [ 10 | "cli.js", 11 | "dist" 12 | ], 13 | "scripts": { 14 | "prepare": "cp package.json package.generated.json", 15 | "dev": "vite", 16 | "build": "unbuild", 17 | "test": "vitest", 18 | "preview": "vite preview" 19 | }, 20 | "main": "./dist/index.cjs", 21 | "module": "./dist/index.mjs", 22 | "types": "./dist/index.d.ts", 23 | "exports": { 24 | ".": { 25 | "import": "./dist/index.mjs", 26 | "require": "./dist/index.cjs", 27 | "types": "./dist/index.d.ts" 28 | }, 29 | "./vite": { 30 | "import": "./dist/vite.mjs", 31 | "require": "./dist/vite.cjs", 32 | "types": "./dist/vite.d.ts" 33 | }, 34 | "./package.json": "./package.generated.json", 35 | "./*": { 36 | "import": "./submodules/*/index.js" 37 | } 38 | }, 39 | "devDependencies": { 40 | "@types/node": "20.5.1", 41 | "typescript": "^5.0.2", 42 | "vite": "4.4.9", 43 | "vitest": "0.34.2" 44 | }, 45 | "dependencies": { 46 | "@sindresorhus/slugify": "^2.2.1", 47 | "c12": "^1.5.1", 48 | "chalk": "^5.3.0", 49 | "commander": "^11.0.0", 50 | "execa": "^8.0.1", 51 | "glob": "^10.3.3", 52 | "minimatch": "^9.0.3", 53 | "unbuild": "2.0.0" 54 | } 55 | } -------------------------------------------------------------------------------- /packages/soori/src/build/__tests__/runPlugins.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import { testPlugins } from '../../test'; 3 | import { Plugin } from '../../types'; 4 | 5 | const defaultPlugin: Plugin = { 6 | name: 'test-plugin', 7 | watch: ['some-folder/*'], 8 | output: 'test', 9 | build: ({ slug }) => ({ 10 | id: slug, 11 | content: `export const ${slug} = true;`, 12 | }), 13 | }; 14 | 15 | describe('runPlugins', () => { 16 | it('works with basic setup', async () => { 17 | const outputs = await testPlugins({ 18 | listFiles: () => ['a.md', 'b.md', 'c.md'], 19 | plugins: [defaultPlugin], 20 | }); 21 | expect(outputs).toMatchInlineSnapshot(` 22 | { 23 | "node_modules/soori/submodules/test/a.ts": "export const a = true;", 24 | "node_modules/soori/submodules/test/b.ts": "export const b = true;", 25 | "node_modules/soori/submodules/test/c.ts": "export const c = true;", 26 | "node_modules/soori/submodules/test/index.ts": "export * from './a' 27 | export * from './b' 28 | export * from './c'", 29 | } 30 | `); 31 | }); 32 | 33 | it('emits individual files in .js', async () => { 34 | const outputs = await testPlugins({ 35 | listFiles: () => ['a.md', 'b.md', 'c.md'], 36 | plugins: [ 37 | { 38 | ...defaultPlugin, 39 | build: ({ slug }) => ({ 40 | id: slug, 41 | content: `export const ${slug} = true;`, 42 | ext: 'js', 43 | }), 44 | }, 45 | ], 46 | }); 47 | expect(outputs['node_modules/soori/submodules/test/a.js']).toBeDefined(); 48 | expect(outputs['node_modules/soori/submodules/test/b.js']).toBeDefined(); 49 | expect(outputs['node_modules/soori/submodules/test/c.js']).toBeDefined(); 50 | }); 51 | 52 | it('emits entry file in .js', async () => { 53 | const outputs = await testPlugins({ 54 | listFiles: () => ['a.md', 'b.md', 'c.md'], 55 | plugins: [ 56 | { 57 | ...defaultPlugin, 58 | entry: { 59 | build: ({ filenamesWithoutExt }) => { 60 | return filenamesWithoutExt 61 | .map((filename) => `export * from './${filename}'`) 62 | .join('\n'); 63 | }, 64 | ext: 'js', 65 | }, 66 | }, 67 | ], 68 | }); 69 | expect( 70 | outputs['node_modules/soori/submodules/test/index.js'] 71 | ).toBeDefined(); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /packages/soori/src/build/build.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { exists } from '../utils'; 4 | import { filterPluginsByChangedFile, loadConfig } from './config'; 5 | import { error } from '../utils/log'; 6 | import { runPlugins } from './runPlugins'; 7 | import { InternalPlugin } from '../types'; 8 | 9 | export const build = async ({ 10 | cleanUp, 11 | changedFilePath, 12 | dryRun = false, 13 | cwd = process.cwd(), 14 | }: { 15 | cleanUp: boolean; 16 | changedFilePath?: string; 17 | dryRun?: boolean; 18 | cwd?: string; 19 | }) => { 20 | await prepareGeneratedPackageJson(cwd, cleanUp); 21 | 22 | let loadedConfig = await loadConfig(cwd); 23 | if (!loadedConfig.ok) { 24 | error(loadedConfig.error); 25 | process.exit(1); 26 | } 27 | 28 | const config = loadedConfig.data; 29 | const plugins = filterPluginsByChangedFile({ 30 | plugins: config.plugins, 31 | changedFilename: changedFilePath?.slice(cwd.length + 1), 32 | }); 33 | 34 | await prepareOutputDirs(cwd, cleanUp, plugins); 35 | await runPlugins({ 36 | cwd, 37 | plugins, 38 | dryRun, 39 | changedFilePath, 40 | }); 41 | }; 42 | 43 | async function prepareOutputDirs( 44 | cwd: string, 45 | cleanUp: boolean, 46 | plugins: InternalPlugin[] 47 | ) { 48 | for (const plugin of plugins) { 49 | const dir = path.resolve(cwd, plugin.output.dir); 50 | if (await exists(dir)) { 51 | if (cleanUp) { 52 | await fs.rm(dir, { recursive: true, force: true }); 53 | await fs.mkdir(dir, { recursive: true }); 54 | } 55 | } else { 56 | await fs.mkdir(dir, { recursive: true }); 57 | } 58 | } 59 | } 60 | 61 | async function prepareGeneratedPackageJson(cwd: string, cleanUp: boolean) { 62 | const src = path.resolve(cwd, 'node_modules', 'soori', 'package.json'); 63 | const dest = path.resolve( 64 | cwd, 65 | 'node_modules', 66 | 'soori', 67 | 'package.generated.json' 68 | ); 69 | if (cleanUp || !(await exists(dest))) { 70 | await fs.cp(src, dest); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/soori/src/build/config.ts: -------------------------------------------------------------------------------- 1 | import { loadConfig as _loadConfig } from 'c12'; 2 | import { minimatch } from 'minimatch'; 3 | import type { Config, InternalConfig, InternalPlugin, Result } from '../types'; 4 | import { submodule } from './output'; 5 | 6 | export const loadConfig = async ( 7 | cwd: string 8 | ): Promise> => { 9 | const { config } = await _loadConfig({ 10 | cwd, 11 | name: 'soori', 12 | }); 13 | if (!config) { 14 | return { 15 | ok: false, 16 | error: 'Configuration missing. Create `soori.config.js`.', 17 | }; 18 | } 19 | 20 | return { 21 | ok: true, 22 | data: resolveConfig(config), 23 | }; 24 | }; 25 | 26 | export const resolveConfig = (config: Config) => { 27 | const internalConfig: InternalConfig = { 28 | plugins: config.plugins.map((plugin) => { 29 | const entry = 30 | typeof plugin.entry === 'function' 31 | ? { build: plugin.entry, ext: 'ts' } 32 | : { 33 | build: 34 | plugin.entry?.build ?? 35 | (({ 36 | filenamesWithoutExt, 37 | }: { 38 | filenames: string[]; 39 | filenamesWithoutExt: string[]; 40 | }) => { 41 | return filenamesWithoutExt 42 | .map( 43 | (filenameWithoutExt) => 44 | `export * from './${filenameWithoutExt}'` 45 | ) 46 | .join('\n'); 47 | }), 48 | ext: plugin.entry?.ext ?? 'ts', 49 | }; 50 | const output = 51 | typeof plugin.output === 'string' 52 | ? submodule({ name: plugin.output, ext: entry.ext }) 53 | : plugin.output; 54 | 55 | return { 56 | ...plugin, 57 | entry, 58 | output, 59 | }; 60 | }), 61 | }; 62 | 63 | return internalConfig; 64 | }; 65 | 66 | export const filterPluginsByChangedFile = ({ 67 | plugins, 68 | changedFilename, 69 | }: { 70 | plugins: InternalPlugin[]; 71 | changedFilename?: string; 72 | }) => { 73 | if (!changedFilename) { 74 | return plugins; 75 | } 76 | 77 | return plugins.filter((plugin) => 78 | plugin.watch.some((pattern) => minimatch(changedFilename, pattern)) 79 | ); 80 | }; 81 | -------------------------------------------------------------------------------- /packages/soori/src/build/index.ts: -------------------------------------------------------------------------------- 1 | export * from './build'; 2 | export * from './config'; 3 | export * from './runPlugins'; 4 | export * from './output'; 5 | -------------------------------------------------------------------------------- /packages/soori/src/build/output.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import fs from 'node:fs/promises'; 3 | import { PluginOutput } from '../types'; 4 | import { build } from 'unbuild'; 5 | 6 | type Submodule = (params: { name: string; ext: string }) => PluginOutput; 7 | 8 | export const submodule: Submodule = ({ name, ext }) => ({ 9 | name, 10 | dir: path.resolve('node_modules', 'soori', 'submodules', name), 11 | onBuildStart: async ({ name, dryRun }) => { 12 | if (dryRun) { 13 | return; 14 | } 15 | const packageJsonPath = path.resolve( 16 | 'node_modules', 17 | 'soori', 18 | 'package.generated.json' 19 | ); 20 | const pkg = JSON.parse((await fs.readFile(packageJsonPath)).toString()); 21 | if (ext === 'ts') { 22 | pkg.exports[`./${name}`] = { 23 | import: `./submodules/${name}/dist/index.mjs`, 24 | require: `./submodules/${name}/dist/index.cjs`, 25 | types: `./submodules/${name}/dist/index.d.ts`, 26 | }; 27 | } else { 28 | pkg.exports[`./${name}`] = { 29 | import: `./submodules/${name}/index.${ext}`, 30 | }; 31 | } 32 | await fs.writeFile(packageJsonPath, JSON.stringify(pkg, null, 2)); 33 | }, 34 | onBuildEnd: async ({ dryRun, dir }) => { 35 | if (dryRun) { 36 | return; 37 | } 38 | if (ext === 'ts') { 39 | await build(dir, false, { 40 | entries: ['./index.ts'], 41 | declaration: true, 42 | rollup: { 43 | emitCJS: true, 44 | }, 45 | }); 46 | } 47 | }, 48 | }); 49 | 50 | // export const virtualModule: OutputHelper = (name: string) => ({ 51 | // name, 52 | 53 | // }); 54 | -------------------------------------------------------------------------------- /packages/soori/src/build/runPlugins.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { glob } from 'glob'; 3 | import { execaCommand } from 'execa'; 4 | import slugify from '@sindresorhus/slugify'; 5 | import type { InternalPlugin, BuildOutputs, MaybePromise } from '../types'; 6 | import { info } from '../utils/log'; 7 | import fs from 'node:fs/promises'; 8 | 9 | // const getExecaCommand = (dir: string) => { 10 | // const cmd = (command: string, options: Options = {}) => { 11 | // return execaCommand(command, { 12 | // cwd: dir, 13 | // ...options, 14 | // }); 15 | // }; 16 | // return cmd as typeof execaCommand; 17 | // }; 18 | 19 | export const runPlugins = async ({ 20 | cwd, 21 | plugins, 22 | dryRun, 23 | listFiles = ({ patterns, cwd }) => glob(patterns, { cwd }), 24 | changedFilePath, 25 | }: { 26 | cwd: string; 27 | plugins: InternalPlugin[]; 28 | dryRun: boolean; 29 | listFiles?: (params: { 30 | patterns: string[]; 31 | cwd: string; 32 | }) => MaybePromise; 33 | changedFilePath?: string; 34 | }) => { 35 | const outputs: BuildOutputs = {}; 36 | const saveOutput = async ({ 37 | plugin, 38 | filename, 39 | content, 40 | }: { 41 | plugin: InternalPlugin; 42 | filename: string; 43 | content: string; 44 | }) => { 45 | const filePath = path.resolve(cwd, plugin.output.dir, filename); 46 | outputs[filePath] = content; 47 | 48 | if (!dryRun) { 49 | await fs.mkdir(path.dirname(filePath), { recursive: true }); 50 | await fs.writeFile(filePath, content); 51 | info(`> Generated: ${filePath}`); 52 | } 53 | }; 54 | 55 | for (const plugin of plugins) { 56 | info(`Applying plugin \`${plugin.name}\`...`); 57 | await plugin.output.onBuildStart({ 58 | name: plugin.output.name, 59 | dir: path.resolve(cwd, plugin.output.dir), 60 | dryRun, 61 | execaCommand, 62 | }); 63 | 64 | const filePaths = (await listFiles({ patterns: plugin.watch, cwd })).map( 65 | (filename) => path.resolve(cwd, filename) 66 | ); 67 | const filenames = filePaths.map((filePath) => path.basename(filePath)); 68 | const filenamesWithoutExt = filenames.map((filename) => { 69 | const extname = path.extname(filename); 70 | return filename.slice(0, filename.length - extname.length); 71 | }); 72 | await saveOutput({ 73 | plugin, 74 | filename: `index.${plugin.entry.ext}`, 75 | content: plugin.entry.build({ 76 | filePaths, 77 | filenames, 78 | filenamesWithoutExt, 79 | }), 80 | }); 81 | 82 | for (const filePath of changedFilePath ? [changedFilePath] : filePaths) { 83 | const filename = path.basename(filePath); 84 | const ext = path.extname(filePath).slice(1); 85 | const filenameWithoutExt = filename.slice( 86 | 0, 87 | filename.length - ext.length - 1 88 | ); 89 | const slug = slugify( 90 | filePath.slice( 91 | cwd.endsWith('/') ? cwd.length : cwd.length + 1, 92 | filePath.length - ext.length - 1 93 | ) 94 | ); 95 | let outputs = await plugin.build({ 96 | filePath, 97 | filename, 98 | filenameWithoutExt, 99 | slug, 100 | ext, 101 | }); 102 | if (!outputs) { 103 | outputs = []; 104 | } 105 | if (!Array.isArray(outputs)) { 106 | outputs = [outputs]; 107 | } 108 | for (const output of outputs) { 109 | await saveOutput({ 110 | plugin, 111 | filename: `${output.id}.${output.ext ?? 'ts'}`, 112 | content: output.content, 113 | }); 114 | } 115 | } 116 | 117 | await plugin.output.onBuildEnd({ 118 | name: plugin.output.name, 119 | dir: path.resolve(cwd, plugin.output.dir), 120 | dryRun, 121 | execaCommand, 122 | }); 123 | } 124 | 125 | return outputs; 126 | }; 127 | -------------------------------------------------------------------------------- /packages/soori/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { Command } from 'commander'; 2 | import { description, version } from '../package.json'; 3 | import { build } from './build'; 4 | 5 | const createProgram = () => { 6 | const program = new Command(); 7 | program.name('Soori').description(description).version(version); 8 | 9 | program 10 | .command('build') 11 | .option('--no-clean', 'Do not clean the output directory') 12 | .option('-D, --dry-run', 'Dry run') 13 | .action(async (_, options) => { 14 | await build({ 15 | cleanUp: !options.noClean, 16 | dryRun: Boolean(options.dryRun), 17 | }); 18 | }); 19 | 20 | return program; 21 | }; 22 | 23 | export const main = () => { 24 | const program = createProgram(); 25 | program.parse(); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/soori/src/config.ts: -------------------------------------------------------------------------------- 1 | import { Config } from './types'; 2 | 3 | export const defineSooriConfig = (config: Config) => config; 4 | -------------------------------------------------------------------------------- /packages/soori/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './vite'; 2 | export * from './config'; 3 | export * from './plugin'; 4 | export * from './types'; 5 | export * from './build'; 6 | export * from './test'; 7 | -------------------------------------------------------------------------------- /packages/soori/src/plugin.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from './types'; 2 | 3 | export const defineSooriPlugin = (plugin: Plugin) => plugin; 4 | -------------------------------------------------------------------------------- /packages/soori/src/test.ts: -------------------------------------------------------------------------------- 1 | import { runPlugins, resolveConfig } from './build'; 2 | import { BuildOutputs, Plugin } from './types'; 3 | 4 | type Params = Omit< 5 | Parameters[0], 6 | 'cwd' | 'dryRun' | 'plugins' 7 | > & { 8 | cwd?: string; 9 | plugins: Plugin[]; 10 | }; 11 | 12 | export const testPlugins = async ({ 13 | cwd = process.cwd(), 14 | plugins, 15 | changedFilePath, 16 | listFiles, 17 | }: Params) => { 18 | const config = resolveConfig({ 19 | plugins, 20 | }); 21 | 22 | const outputs = await runPlugins({ 23 | cwd, 24 | plugins: config.plugins, 25 | changedFilePath, 26 | listFiles, 27 | dryRun: true, 28 | }); 29 | 30 | return simplifyOutputs({ outputs, cwd }); 31 | }; 32 | 33 | export const simplifyOutputs = ({ 34 | outputs, 35 | cwd, 36 | }: { 37 | outputs: BuildOutputs; 38 | cwd: string; 39 | }) => 40 | Object.keys(outputs).reduce((acc, filePath) => { 41 | acc[filePath.slice(cwd.endsWith('/') ? cwd.length : cwd.length + 1)] = 42 | outputs[filePath]; 43 | return acc; 44 | }, {}); 45 | -------------------------------------------------------------------------------- /packages/soori/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { execaCommand } from 'execa'; 2 | type ExecaCommand = typeof execaCommand; 3 | 4 | export type MaybePromise = T | Promise; 5 | export type MaybeArray = T | Array; 6 | 7 | export type Result = 8 | | { 9 | ok: true; 10 | data: Data; 11 | } 12 | | { 13 | ok: false; 14 | error: Err; 15 | }; 16 | 17 | export type BuildOutputs = { 18 | [fileName: string]: string; 19 | }; 20 | 21 | export type BuildOutput = { 22 | fileName: string; 23 | content: string; 24 | }; 25 | 26 | // export type BuildAll = { 27 | // watch?: string[]; 28 | // handle: () => BuildOutput | Promise; 29 | // }; 30 | 31 | // export type BuildPerEachFile = { 32 | // watch: string[]; 33 | // handleEach: (params: { 34 | // fullPath: string; 35 | // fileName: string; 36 | // fileNameWithoutExt: string; 37 | // }) => BuildOutput | Promise; 38 | // }; 39 | 40 | // export type Build = BuildAll | BuildPerEachFile; 41 | 42 | // export type InternalOutput = { 43 | // dir: string; 44 | // packageExports?: Partial<{ 45 | // import: string; 46 | // require: string; 47 | // types: string; 48 | // }>; 49 | // onBuildStart?: (opts: { 50 | // dir: string; 51 | // execaCommand: ExecaCommand; 52 | // }) => Promise; 53 | // onBuildEnd?: (opts: { 54 | // dir: string; 55 | // execaCommand: ExecaCommand; 56 | // }) => Promise; 57 | // }; 58 | 59 | // export type Output = Partial; 60 | 61 | export type Config = { 62 | plugins: Plugin[]; 63 | }; 64 | 65 | export type InternalConfig = { 66 | plugins: InternalPlugin[]; 67 | }; 68 | 69 | export type Plugin = { 70 | name: string; 71 | watch: string[]; 72 | build: PluginBuild; 73 | 74 | output: string | PluginOutput; 75 | entry?: PluginEntryBuild | Partial; 76 | }; 77 | 78 | export type InternalPlugin = { 79 | name: string; 80 | watch: string[]; 81 | build: PluginBuild; 82 | 83 | output: PluginOutput; 84 | entry: PluginEntry; 85 | }; 86 | 87 | export type PluginOutput = { 88 | name: string; 89 | dir: string; 90 | onBuildStart: (params: { 91 | name: string; 92 | dir: string; 93 | dryRun: boolean; 94 | execaCommand: ExecaCommand; 95 | }) => MaybePromise; 96 | onBuildEnd: (params: { 97 | name: string; 98 | dir: string; 99 | dryRun: boolean; 100 | execaCommand: ExecaCommand; 101 | }) => MaybePromise; 102 | }; 103 | 104 | type PluginEntry = { 105 | build: PluginEntryBuild; 106 | ext: string; 107 | }; 108 | 109 | type PluginEntryBuild = (params: { 110 | filePaths: string[]; 111 | filenames: string[]; 112 | filenamesWithoutExt: string[]; 113 | }) => string; 114 | 115 | type PluginBuild = (params: { 116 | filePath: string; 117 | filename: string; 118 | filenameWithoutExt: string; 119 | slug: string; 120 | ext: string; 121 | }) => MaybePromise< 122 | | MaybeArray<{ 123 | id: string; 124 | content: string; 125 | ext?: string; 126 | }> 127 | | undefined 128 | >; 129 | -------------------------------------------------------------------------------- /packages/soori/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | 3 | export const exists = async (path: string) => { 4 | try { 5 | await fs.access(path, fs.constants.F_OK); 6 | return true; 7 | } catch (_e) { 8 | return false; 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /packages/soori/src/utils/log.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | // const primary = chalk.hex('#DE4500'); 4 | 5 | export const info = (message: string) => { 6 | console.log('[Soori]', chalk.cyan('[Info]'), message); 7 | }; 8 | 9 | export const error = (message: string) => { 10 | console.log('[Soori]', chalk.bold.red('[ERROR]'), message); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/soori/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/soori/src/vite.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from 'vite'; 2 | 3 | import { build } from './build'; 4 | 5 | export const soori = (): Plugin => { 6 | return { 7 | name: 'vite-plugin-soori', 8 | buildStart: async () => { 9 | await build({ cleanUp: true }); 10 | }, 11 | configureServer: (server) => { 12 | const listener = async (filePath: string) => { 13 | await build({ 14 | cleanUp: false, 15 | changedFilePath: filePath, 16 | }); 17 | }; 18 | 19 | server.watcher.on('add', listener); 20 | server.watcher.on('change', listener); 21 | }, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/soori/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/soori/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/*' 3 | - 'packages/*' 4 | -------------------------------------------------------------------------------- /scripts/release.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node_modules/.bin/tsx 2 | // https://github.com/google/zx/issues/467#issuecomment-1577838056 3 | 4 | import { $ } from 'zx'; 5 | import fs from 'fs/promises'; 6 | 7 | await $`cd packages/soori && npx changelogen@latest --bump`; 8 | await $`cp packages/soori/CHANGELOG.md .`; 9 | 10 | const { name, version } = JSON.parse( 11 | (await fs.readFile('packages/soori/package.json')).toString() 12 | ); 13 | await $`pnpm install`; 14 | await $`pnpm run build:lib`; 15 | await $`git add . && git commit -m "chore(${name}): release v${version}"`; 16 | 17 | console.log('Run the following command to publish:'); 18 | console.log(' > cd packages/soori && npm publish'); 19 | console.log(''); 20 | console.log('Run the following command to tag:'); 21 | console.log( 22 | ` > git tag ${name}@${version} && git push origin ${name}@${version}` 23 | ); 24 | --------------------------------------------------------------------------------