├── .changeset └── config.json ├── .editorconfig ├── .github └── workflows │ ├── main.yml │ ├── release-pull-request.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .pnpmfile.cjs ├── .vscode ├── extensions.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTING.zh-CN.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── biome.json ├── document ├── en │ ├── api │ │ ├── app.md │ │ ├── ejs.md │ │ ├── fs.md │ │ ├── git.md │ │ ├── handlebars.md │ │ ├── index.md │ │ ├── input.md │ │ ├── json.md │ │ └── npm.md │ ├── concept.md │ └── start.md └── zh │ ├── api │ ├── app.md │ ├── ejs.md │ ├── fs.md │ ├── git.md │ ├── handlebars.md │ ├── index.md │ ├── input.md │ ├── json.md │ └── npm.md │ ├── concept.md │ └── start.md ├── modern.config.js ├── monorepo.code-workspace ├── package.json ├── packages ├── api │ ├── app │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ ├── locale │ │ │ │ ├── en.ts │ │ │ │ ├── index.ts │ │ │ │ └── zh.ts │ │ │ └── utils │ │ │ │ ├── checkUseNvm.ts │ │ │ │ └── transform.ts │ │ └── tsconfig.json │ ├── ejs │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── index.ts │ │ │ │ └── renderString.ts │ │ ├── tests │ │ │ ├── tsconfig.json │ │ │ └── utils.test.ts │ │ └── tsconfig.json │ ├── fs │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── git │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ └── index.ts │ │ ├── tests │ │ │ ├── index.test.ts │ │ │ └── tsconfig.json │ │ └── tsconfig.json │ ├── handlebars │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── index.ts │ │ │ │ └── renderString.ts │ │ ├── tests │ │ │ ├── tsconfig.json │ │ │ └── utils.test.ts │ │ └── tsconfig.json │ ├── json │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ └── index.ts │ │ ├── tests │ │ │ ├── index.test.ts │ │ │ └── tsconfig.json │ │ └── tsconfig.json │ └── npm │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── modern.config.ts │ │ ├── package.json │ │ ├── src │ │ ├── index.ts │ │ └── utils │ │ │ ├── index.ts │ │ │ └── install.ts │ │ ├── tests │ │ ├── tsconfig.json │ │ └── utils.test.ts │ │ └── tsconfig.json ├── cli │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bin │ │ └── run.js │ ├── modern.config.ts │ ├── package.json │ ├── src │ │ ├── actions │ │ │ └── genAction.ts │ │ ├── index.ts │ │ └── utils │ │ │ └── index.ts │ └── tsconfig.json ├── core │ ├── .npmignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── modern.config.ts │ ├── package.json │ ├── src │ │ ├── codesmith │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ ├── constants.ts │ │ ├── generator │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── logger │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ ├── materials │ │ │ ├── FsMaterial.ts │ │ │ ├── FsResource.ts │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ └── utils │ │ │ ├── downloadPackage.ts │ │ │ ├── fsExists.ts │ │ │ ├── getGeneratorDir.ts │ │ │ ├── getNpmPackageInfo.ts │ │ │ ├── getNpmRegistry.ts │ │ │ ├── getNpmTarballUrl.ts │ │ │ ├── getNpmVersion.ts │ │ │ ├── getPackageInfo.ts │ │ │ ├── index.ts │ │ │ ├── nodeRequire.ts │ │ │ ├── packageManager.ts │ │ │ └── timeoutPromise.ts │ ├── tests │ │ ├── tsconfig.json │ │ └── utils │ │ │ ├── downloadPackage.test.ts │ │ │ ├── fsExists.test.ts │ │ │ ├── getGeneratorDir.test.ts │ │ │ ├── getNpmVersion.test.ts │ │ │ ├── getNpmtarballUrl.test.ts │ │ │ └── getPackageInfo.test.ts │ └── tsconfig.json ├── formily │ ├── .npmignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── modern.config.ts │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── inquirer.ts │ │ ├── prompt.ts │ │ └── transform.ts │ ├── tests │ │ ├── prompt.test.ts │ │ ├── transform.test.ts │ │ └── tsconfig.json │ └── tsconfig.json ├── global │ ├── .npmignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── modern.config.ts │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── inquirer │ ├── .npmignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── modern.config.ts │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── list.ts │ │ ├── types │ │ │ └── index.d.ts │ │ └── utils │ │ │ ├── index.ts │ │ │ └── pointer.ts │ └── tsconfig.json └── utils │ ├── .npmignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── modern.config.ts │ ├── package.json │ ├── src │ ├── chalk.ts │ ├── execa.ts │ ├── fs-extra.ts │ ├── glob.ts │ ├── index.ts │ ├── lodash.ts │ ├── npm.ts │ ├── ora.ts │ └── semver.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── .eslintrc.js ├── package.json ├── src │ └── get-release-version.ts └── tsconfig.json └── tsconfig.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.6.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [["@modern-js/*"]], 6 | "access": "restricted", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": [], 10 | "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { 11 | "onlyUpdatePeerDependentsWhenOutOfRange": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_style = space 9 | insert_final_newline = true 10 | 11 | [*.{js,jsx,ts,tsx,mjs,mjsx,cjs,cjsx,sh,rb}] 12 | indent_size = 2 13 | max_line_length = 80 14 | trim_trailing_whitespace = true 15 | 16 | [*.py] 17 | indent_size = 4 18 | max_line_length = 80 19 | trim_trailing_whitespace = true 20 | 21 | [*.{css,scss,less,html,hbs,ejs,json,code-workspace,yml,yaml,gql}] 22 | indent_size = 2 23 | trim_trailing_whitespace = true 24 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [main] 10 | pull_request: 11 | branches: [main] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | test: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 1 29 | 30 | - name: git init 31 | run: git config --global user.email "modern@bytedance.com" && git config --global user.name "modern" 32 | 33 | # Runs a single command using the runners shell 34 | - name: Install Pnpm 35 | run: npm install -g --force corepack && corepack enable 36 | 37 | - name: install 38 | run: pnpm i --ignore-scripts 39 | 40 | - name: build 41 | run: pnpm run prepare 42 | 43 | - name: lint 44 | run: pnpm run lint 45 | 46 | - name: test 47 | run: pnpm -r test 48 | -------------------------------------------------------------------------------- /.github/workflows/release-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Release Pull Request 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | type: choice 8 | description: 'Release Type(canary, alpha, beta, latest)' 9 | required: true 10 | default: 'latest' 11 | options: 12 | - canary 13 | - alpha 14 | - beta 15 | - latest 16 | 17 | jobs: 18 | release: 19 | name: Create Release Pull Request 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout Repo 23 | uses: actions/checkout@master 24 | with: 25 | # This makes Actions fetch only one branch to release 26 | fetch-depth: 100 27 | 28 | - name: Install Pnpm 29 | run: npm install -g --force corepack && corepack enable 30 | 31 | - name: Setup Node.js 18 32 | uses: actions/setup-node@v3 33 | with: 34 | node-version: '18' 35 | cache: 'pnpm' 36 | 37 | - name: Install Dependencies 38 | run: pnpm install --ignore-scripts 39 | 40 | - name: Create Release Pull Request 41 | uses: web-infra-dev/actions@v2 42 | with: 43 | # this expects you to have a script called release which does a build for your packages and calls changeset publish 44 | version: ${{ github.event.inputs.version }} 45 | versionNumber: 'auto' 46 | type: 'pull request' 47 | tools: 'modern' 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} 50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 51 | REPOSITORY: ${{ github.repository }} 52 | REF: ${{ github.ref }} 53 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | type: choice 8 | description: 'Release Version(canary, alpha, beta, latest)' 9 | required: true 10 | default: 'canary' 11 | options: 12 | - canary 13 | - alpha 14 | - beta 15 | - latest 16 | branch: 17 | description: 'Release Branch(confirm release branch)' 18 | required: true 19 | default: 'main' 20 | 21 | permissions: 22 | id-token: write 23 | 24 | jobs: 25 | release: 26 | name: Release 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout Repo 30 | uses: actions/checkout@v3 31 | with: 32 | # This makes Actions fetch only one branch to release 33 | fetch-depth: 1 34 | 35 | - name: Install Pnpm 36 | run: npm install -g --force corepack && corepack enable 37 | 38 | - name: Setup Node.js 18 39 | uses: actions/setup-node@v3 40 | with: 41 | node-version: '18' 42 | cache: 'pnpm' 43 | 44 | - name: Install Dependencies 45 | run: pnpm install --ignore-scripts 46 | 47 | - name: Prepare 48 | run: pnpm run prepare 49 | 50 | - name: Release 51 | uses: web-infra-dev/actions@v2 52 | with: 53 | # this expects you to have a script called release which does a build for your packages and calls changeset publish 54 | version: ${{ github.event.inputs.version }} 55 | branch: ${{ github.event.inputs.branch }} 56 | type: 'release' 57 | tools: 'modern' 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} 60 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 61 | REPOSITORY: ${{ github.repository }} 62 | REF: ${{ github.ref }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | dist/ 26 | coverage/ 27 | release/ 28 | output/ 29 | output_resource/ 30 | 31 | .vscode/**/* 32 | !.vscode/settings.json 33 | !.vscode/extensions.json 34 | .idea/ 35 | 36 | **/*/typings/auto-generated 37 | **/*/adapters/**/index.ts 38 | **/*/adapters/**/index.js 39 | 40 | .changeset/pre.json 41 | .pnpm-store/ 42 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry = 'https://registry.npmjs.org/' 2 | 3 | link-workspace-packages=false 4 | strict-peer-dependencies=false 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/gallium 2 | -------------------------------------------------------------------------------- /.pnpmfile.cjs: -------------------------------------------------------------------------------- 1 | function readPackage(pkg, _context) { 2 | // Override the manifest of foo@1.x after downloading it from the registry 3 | // fix @samverschueren/stream-to-observable bug 4 | if (pkg.name === '@samverschueren/stream-to-observable') { 5 | pkg.dependencies = { 6 | ...pkg.dependencies, 7 | 'any-observable': '^0.5.1', 8 | }; 9 | } 10 | 11 | return pkg; 12 | } 13 | 14 | module.exports = { 15 | hooks: { 16 | readPackage, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "styled-components.vscode-styled-components", 4 | "cpylua.language-postcss", 5 | "EditorConfig.editorconfig", 6 | "drKnoxy.eslint-disable-snippets", 7 | "mkaufman.htmlhint", 8 | "streetsidesoftware.code-spell-checker", 9 | "codezombiech.gitignore", 10 | "aaron-bond.better-comments", 11 | "jasonnutter.search-node-modules", 12 | "jock.svg", 13 | "mikestead.dotenv", 14 | "vscode-icons-team.vscode-icons", 15 | "biomejs.biome" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | ".code-workspace": "jsonc", 4 | ".babelrc": "json", 5 | ".eslintrc": "jsonc", 6 | ".eslintrc*.json": "jsonc", 7 | ".stylelintrc": "jsonc", 8 | "stylelintrc": "jsonc", 9 | ".htmlhintrc": "jsonc", 10 | "htmlhintrc": "jsonc", 11 | "Procfile*": "shellscript", 12 | "README": "markdown", 13 | "*.ttml": "xml", 14 | "*.ttss": "css" 15 | }, 16 | "search.useIgnoreFiles": true, 17 | "search.exclude": { 18 | "**/dist": true, 19 | "**/*.log": true, 20 | "**/*.pid": true, 21 | "**/.git": true, 22 | "**/compiled": true, 23 | "**/coverage": true, 24 | "**/node_modules": true, 25 | "**/bower_components": true 26 | }, 27 | // 28 | "editor.rulers": [80, 120], 29 | "files.eol": "\n", 30 | "files.trimTrailingWhitespace": true, 31 | "files.insertFinalNewline": true, 32 | // 33 | "cSpell.diagnosticLevel": "Hint", 34 | "javascript.validate.enable": false, 35 | "typescript.validate.enable": true, 36 | "css.validate": false, 37 | "scss.validate": false, 38 | "less.validate": false, 39 | "editor.defaultFormatter": "biomejs.biome", 40 | "editor.formatOnSave": true, 41 | "editor.codeActionsOnSave": { 42 | "quickfix.biome": "explicit" 43 | }, 44 | "javascript.format.enable": false, 45 | "typescript.format.enable": false, 46 | // 47 | "json.format.enable": false, 48 | "emmet.triggerExpansionOnTab": true, 49 | "typescript.tsdk": "node_modules/typescript/lib", 50 | "files.exclude": { 51 | "**/.git": true, 52 | "**/.svn": true, 53 | "**/.hg": true, 54 | "**/CVS": true, 55 | "**/.DS_Store": true, 56 | "**/Thumbs.db": true, 57 | "**/node_modules": false 58 | }, 59 | "[typescript]": { 60 | "editor.defaultFormatter": "biomejs.biome" 61 | }, 62 | "[typescriptreact]": { 63 | "editor.defaultFormatter": "biomejs.biome" 64 | }, 65 | "[json]": { 66 | "editor.defaultFormatter": "biomejs.biome" 67 | }, 68 | "[jsonc]": { 69 | "editor.defaultFormatter": "esbenp.prettier-vscode" 70 | }, 71 | "[javascriptreact]": { 72 | "editor.defaultFormatter": "biomejs.biome" 73 | }, 74 | "[javascript]": { 75 | "editor.defaultFormatter": "biomejs.biome" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

CodeSmith

6 | 7 |

8 | A cool code generation tool. 9 |

10 | 11 |

12 | npm version 13 | downloads 14 | License 15 |

16 | 17 | English | [简体中文](./README.zh-CN.md) 18 | 19 | ## Introduce 20 | 21 | CodeSmith is a code generation tool that uses the concept of micro-generators to complete the entire code generation process. 22 | 23 | Traditional scaffolding usually provides project-level generators based on templates, which are used once and then discarded. After generating the project, they do not help with subsequent business iterations. 24 | 25 | Micro-generators focus on the entire lifecycle of the project. They can generate file modules of various granularities and types in the project (such as entry, component, model, etc.), and can also generate abstract business logic (which may not create new files, but automatically refactor existing files). Micro-generators are not one-time tools used only when creating projects, but a toolbox that accompanies the project's subsequent iterative process. 26 | 27 | [`@modern-js/create`](https://www.npmjs.com/package/@modern-js/create) is based on CodeSmith and dynamically generates different initial files and codes or modifies and restructures existing files and codes based on different micro-generators loaded on demand during the Q&A process. 28 | 29 | The new command provided by Modern.js is also based on CodeSmith and is used to create project elements and enable functions during the project development process. 30 | 31 | ## Document 32 | 33 | - [Quick Start](./document/en/start.md) 34 | - [Concepts](./document/en/concept.md) 35 | - [API](./document/en/api/index.md) 36 | 37 | ## Contributing 38 | 39 | > New contributors welcome! 40 | 41 | Please read the [Contributing Guide](https://github.com/web-infra-dev/codesmith/blob/main/CONTRIBUTING.md). 42 | 43 | ### Code of Conduct 44 | 45 | This repo has adopted the Bytedance Open Source Code of Conduct. Please check [Code of Conduct](./CODE_OF_CONDUCT.md) for more details. 46 | 47 | ## License 48 | 49 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/codesmith/blob/main/LICENSE). 50 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

CodeSmith

6 | 7 |

8 | A cool code generation tool. 9 |

10 | 11 |

12 | npm version 13 | downloads 14 | License 15 |

16 | 17 | [English](./README.md) | 简体中文 18 | 19 | ## 介绍 20 | 21 | CodeSmith 是一个代码生成工具,使用微生成器的理念来完成整个代码生成过程。 22 | 23 | 传统脚手架通常提供整个项目级别的生成器,基于模板,一用即抛,生成完项目之后,对后续的业务迭代没有帮助。 24 | 25 | 微生成器关注的是项目的整个生命周期,既可以生成项目中各种粒度各种类型的文件模块(比如 entry、component、model 等),也可以生成抽象的业务逻辑(可能不创建新文件,而是在现有文件上做自动重构)。微生成器不是只在最初创建项目的时候使用的一次性工具,而是伴随项目后续迭代过程的工具箱。 26 | 27 | [`@modern-js/create`](https://www.npmjs.com/package/@modern-js/create) 是基于 CodeSmith 实现的,会在问答过程中,按需加载不同的微生成器,动态生成不同的初始文件和代码或修改重组已有的文件和代码。 28 | 29 | Modern.js 提供的 `new` 命令也是基于 CodeSmith 实现的,用于项目开发过程中新建项目元素、开启功能等。 30 | 31 | ## 文档 32 | 33 | - [快速上手](./document/zh/start.md) 34 | - [核心概念](./document/zh/concept.md) 35 | - [API](./document/zh/api/index.md) 36 | 37 | ## 参与贡献 38 | 39 | > 欢迎参与 CodeSmith 贡献! 40 | 41 | 请阅读 [贡献指南](https://github.com/web-infra-dev/codesmith/blob/main/CONTRIBUTING.zh-CN.md) 来共同参与 CodeSmith 的建设。 42 | 43 | ### 行为准则 44 | 45 | 本仓库采纳了字节跳动的开源项目行为准则。请点击 [行为准则](./CODE_OF_CONDUCT.md) 查看更多的信息。 46 | 47 | ## License 48 | 49 | CodeSmith 项目基于 [MIT 协议](https://github.com/web-infra-dev/codesmith/blob/main/LICENSE),请自由地享受和参与开源。 50 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 | "vcs": { 4 | "enabled": true, 5 | "defaultBranch": "main", 6 | "clientKind": "git", 7 | "useIgnoreFile": true 8 | }, 9 | "formatter": { 10 | "enabled": true, 11 | "indentStyle": "space", 12 | "ignore": ["package.json"] 13 | }, 14 | "javascript": { 15 | "formatter": { 16 | "quoteStyle": "single", 17 | "arrowParentheses": "asNeeded", 18 | "jsxQuoteStyle": "double", 19 | "lineWidth": 80 20 | } 21 | }, 22 | "linter": { 23 | "enabled": true, 24 | "rules": { 25 | "recommended": true, 26 | "style": { 27 | "useNodejsImportProtocol": "off", 28 | "noNonNullAssertion": "off", 29 | "useDefaultParameterLast": "off" 30 | }, 31 | "suspicious": { 32 | "noExplicitAny": "off" 33 | }, 34 | "complexity": { 35 | "noForEach": "off" 36 | } 37 | } 38 | }, 39 | "organizeImports": { 40 | "enabled": true 41 | }, 42 | "files": { 43 | "ignoreUnknown": true, 44 | "ignore": [".vscode/**/*", "node_modules/**/*", "dist/**/*"] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /document/en/api/ejs.md: -------------------------------------------------------------------------------- 1 | # EJS API 2 | 3 | English | [简体中文](../../zh/api/ejs.md) 4 | 5 | The EJS API is provided by the `@modern-js/codesmith-api-ejs` package, which provides methods for rendering single files and folders using [EJS](https://ejs.co/). 6 | 7 | ## Usage 8 | 9 | ```ts 10 | import { EjsAPI } from '@modern-js/codesmith-api-ejs'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const ejsAPI = new EjsAPI(generator); 14 | await ejsAPI.renderTemplate( 15 | context.current!.material.get('templates/a.js.ejs'), 16 | 'b.js', 17 | { name: 'test ejs' }, 18 | ); 19 | }; 20 | ``` 21 | 22 | - Create an instance of EjsAPI with the generator as the parameter. 23 | - Call the API methods provided on the instance. 24 | 25 | ## API 26 | 27 | ### renderTemplate 28 | 29 | Render a single EJS template file. It is defined as follows: 30 | 31 | ```ts 32 | renderTemplate: ( 33 | templateResource: FsResource, 34 | target: string, 35 | parameters?: Record, 36 | ) => Promise; 37 | ``` 38 | 39 | - `templateResource`: The template file, defined by `context.current!.material.get()`. 40 | - `target`: The target file path. 41 | - `parameters`: The values of template variables. When EJS variables exist in the template, define the corresponding variable values. 42 | 43 | ### renderTemplateDir 44 | 45 | Batch render EJS template folders. It is defined as follows: 46 | 47 | ```ts 48 | type TargetFunction = (globMatch: string) => string; 49 | type RenderTemplateDirOptions = { 50 | nodir?: boolean; 51 | dot?: boolean; 52 | ignore?: string | readonly string[]; 53 | parameters?: Record; 54 | }; 55 | renderTemplateDir: ( 56 | material: FsMaterial, 57 | findGlob: string, 58 | target: TargetFunction, 59 | options?: RenderTemplateDirOptions, 60 | ) => Promise; 61 | ``` 62 | 63 | - `material`: The file resources of the current micro-generator, with a value of `context.current!.material`. 64 | - `findGlob`: The matching rule for template files, which supports the [glob](https://www.npmjs.com/package/glob) format. For example, `templates/**/*` matches all files in the templates directory. 65 | - `target`: The target file name, which renames files that meet the criteria. The function takes the file name as its parameter. 66 | - `options.parameters`: The values of template variables. When EJS variables exist in the template, define the corresponding variable values. 67 | - `options.nodir`/`options.dot`/`options.ignore`: glob matching parameters. 68 | 69 | Example: 70 | 71 | ```ts 72 | const ejsAPI = new EjsAPI(generator); 73 | await ejsAPI.renderTemplateDir( 74 | context.current!.material, 75 | 'templates/**/*', 76 | (resourceKey: string) => 77 | resourceKey.replace('templates/', '').replace('.ejs', ''), 78 | { parameters: { name: 'name' } }, 79 | ); 80 | ``` 81 | -------------------------------------------------------------------------------- /document/en/api/fs.md: -------------------------------------------------------------------------------- 1 | # FS API 2 | 3 | English | [简体中文](../../zh/api/fs.md) 4 | 5 | The FS API is provided by the `@modern-js/codesmith-api-fs` package, which provides methods for rendering single files and folders. 6 | 7 | ## Usage 8 | 9 | ```ts 10 | import { FsAPI } from '@modern-js/codesmith-api-fs'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const fsAPI = new FsAPI(generator); 14 | await fsAPI.renderFile( 15 | context.current!.material.get('templates/a.txt'), 16 | 'b.txt', 17 | ); 18 | }; 19 | ``` 20 | 21 | - Create an instance of FsAPI with the generator as the parameter. 22 | - Call the API methods provided on the instance. 23 | 24 | ## API 25 | 26 | ### renderFile 27 | 28 | Render a single file. It is defined as follows: 29 | 30 | ```ts 31 | renderFile: (resource: FsResource, target: string) => Promise; 32 | ``` 33 | 34 | - `resource`: Define by `context.current!.material.get()`. 35 | - `target`: The target file path. 36 | 37 | ### renderDir 38 | 39 | Batch render folders. It is defined as follows: 40 | 41 | ```ts 42 | type TargetFunction = (globMatch: string) => string; 43 | type RenderDirOptions = { 44 | nodir?: boolean; 45 | dot?: boolean; 46 | ignore?: string | readonly string[]; 47 | }; 48 | renderDir: ( 49 | material: FsMaterial, 50 | findGlob: string, 51 | target: TargetFunction, 52 | options?: RenderDirOptions, 53 | ) => Promise; 54 | ``` 55 | 56 | - `material`: The file resources of the current micro-generator, with a value of `context.current!.material`. 57 | - `findGlob`: The matching rule for template files, which supports the [glob](https://www.npmjs.com/package/glob) format. For example, `templates/**/*` matches all files in the templates directory. 58 | - `target`: The target file name, which renames files that meet the criteria. The function takes the file name as its parameter. 59 | - `options.nodir`/`options.dot`/`options.ignore`: glob matching parameters. 60 | 61 | Example: 62 | 63 | ```ts 64 | const fsAPI = new FsAPI(generator); 65 | await fsAPI.renderDir( 66 | context.current!.material, 67 | 'templates/**/*', 68 | (resourceKey: string) => 69 | resourceKey.replace('templates/', ''), 70 | ); 71 | ``` 72 | -------------------------------------------------------------------------------- /document/en/api/git.md: -------------------------------------------------------------------------------- 1 | # Git API 2 | 3 | English | [简体中文](../../zh/api/git.md) 4 | 5 | The Git API is provided by the `@modern-js/codesmith-api-git` package, which provides methods for initializing Git repositories, committing Git commits, etc. 6 | 7 | ## Usage 8 | 9 | ```ts 10 | import { GitAPI } from '@modern-js/codesmith-api-git'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const gitApi = new GitAPI(generatorCore, generatorContext); 14 | await gitApi.initGitRepo(); 15 | }; 16 | ``` 17 | 18 | - Create an instance of GitAPI with the same parameters as the micro-generator. 19 | - Call the API methods provided on the instance. 20 | 21 | ## API 22 | 23 | ### isInGitRepo 24 | 25 | Determine if the current project is a git repository. It is defined as follows: 26 | 27 | ```ts 28 | isInGitRepo: (cwd?: string) => Promise; 29 | ``` 30 | 31 | - `cwd`: The project directory, with a default value of `generator.outputPath`. 32 | 33 | ### initGitRepo 34 | 35 | Initialize the current project as a git repository. It is defined as follows: 36 | 37 | ```ts 38 | initGitRepo(cwd?: string, force?: boolean): Promise; 39 | ``` 40 | 41 | - `cwd`: The project directory, with a default value of `generator.outputPath`. 42 | - `force`: Whether to force initialization if the current directory is already a git repository. 43 | 44 | ### addAndCommit 45 | 46 | Commit current changes. It is defined as follows: 47 | 48 | ```ts 49 | addAndCommit(commitMessage: string, cwd?: string): Promise; 50 | ``` 51 | 52 | - `commitMessage`: The commit message. 53 | - `cwd`: The project directory, with a default value of `generator.outputPath`. 54 | -------------------------------------------------------------------------------- /document/en/api/handlebars.md: -------------------------------------------------------------------------------- 1 | # Handlebars API 2 | 3 | English | [简体中文](../../zh/api/handelbars.md) 4 | 5 | The Handlebars API is provided by the `@modern-js/codesmith-api-handlebars` package, which provides methods for rendering single files and folders using [Handlebars](https://handlebarsjs.com/). 6 | 7 | ## Usages 8 | 9 | ```ts 10 | import { HandlebarsAPI } from '@modern-js/codesmith-api-handlebars'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const handlebarsAPI = new HandlebarsAPI(generator); 14 | await handlebarsAPI.renderTemplate( 15 | context.current!.material.get('templates/a.js.handlebars'), 16 | 'b.js', 17 | { name: 'test handebars' }, 18 | ); 19 | }; 20 | ``` 21 | 22 | - Create an instance of HandlebarsAPI with the generator as the parameter. 23 | - Call the API methods provided on the instance. 24 | 25 | ## API 26 | 27 | ### registerHelp 28 | 29 | Register Handlebars helper code. It is defined as follows: 30 | 31 | ```ts 32 | registerHelp: ( 33 | helpers: Record, 34 | ) => Promise; 35 | ``` 36 | 37 | - `helpers`: Block helper code object. The use of block helper code can be found [here](https://handlebarsjs.com/guide/#block-helpers). 38 | 39 | ### registerPartials 40 | 41 | Register Handlebars code snippets. It is defined as follows: 42 | 43 | ```ts 44 | registerPartials: ( 45 | partials: Record, 46 | ) => Promise; 47 | ``` 48 | 49 | - `partials`: Code snippet object. The use of code snippets can be found [here](https://handlebarsjs.com/guide/#code-snippets). 50 | 51 | ### renderTemplate 52 | 53 | Render a single Handlebars template file. It is defined as follows: 54 | 55 | ```ts 56 | renderTemplate: ( 57 | templateResource: FsResource, 58 | target: string, 59 | parameters?: Record, 60 | ) => Promise; 61 | ``` 62 | 63 | - `templateResource`: The template file, obtained through `context.current!.material.get()`. 64 | - `target`: The target file path. 65 | - `parameters`: The values of template variables. When Handlebars variables exist in the template, define the corresponding variable values. 66 | 67 | ### renderTemplateDir 68 | 69 | Batch render Handlebars template folders. It is defined as follows: 70 | 71 | ```ts 72 | type TargetFunction = (globMatch: string) => string; 73 | type RenderTemplateDirOptions = { 74 | nodir?: boolean; 75 | dot?: boolean; 76 | ignore?: string | readonly string[]; 77 | parameters?: Record; 78 | }; 79 | renderTemplateDir: ( 80 | material: FsMaterial, 81 | findGlob: string, 82 | target: TargetFunction, 83 | options?: RenderTemplateDirOptions, 84 | ) => Promise; 85 | ``` 86 | 87 | - `material`: The file resources of the current micro-generator, with a value of `context.current!.material`. 88 | - `findGlob`: The matching rule for template files, which supports the [glob](https://www.npmjs.com/package/glob) format. For example, `templates/**/*` matches all files in the templates directory. 89 | - `target`: The target file name, which renames files that meet the criteria. The function takes the file name as its parameter. 90 | - `options.parameters`: The values of template variables. When Handlebars variables exist in the template, define the corresponding variable values. 91 | - `options.nodir`/`options.dot`/`options.ignore`: glob matching parameters. 92 | 93 | Example: 94 | 95 | ```ts 96 | const handlebarsAPI = new HandlebarsAPI(generator); 97 | await handlebarsAPI.renderTemplateDir( 98 | context.current!.material, 99 | 'templates/**/*', 100 | (resourceKey: string) => 101 | resourceKey.replace('templates/', '').replace('.handlebars', ''), 102 | { parameters: { name: 'name' } }, 103 | ); 104 | ``` 105 | -------------------------------------------------------------------------------- /document/en/api/index.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | English | [简体中文](../../zh/api/index.md) 4 | 5 | CodeSmith API is provided by different NPM packages, and you can install different dependencies according to your needs to use the API. 6 | 7 | - [App API](./app.md) 8 | 9 | - [Handlebars API](./handlebars.md) 10 | 11 | - [EJS API](./ejs.md) 12 | 13 | - [JSON API](./json.md) 14 | 15 | - [FS API](./fs.md) 16 | 17 | - [Git API](./git.md) 18 | 19 | - [NPM API](./npm.md) 20 | -------------------------------------------------------------------------------- /document/en/api/input.md: -------------------------------------------------------------------------------- 1 | # Input 2 | 3 | English | [简体中文](../../zh/api/input.md) 4 | 5 | Each customized template provides a way to interact with users through Input, which is defined using JSON Schema: 6 | 7 | For example: 8 | 9 | ```js 10 | const schema = { 11 | type: 'object', 12 | properties: { 13 | language: { 14 | type: 'string', 15 | title: 'Please select the programming language:', 16 | enum: [ 17 | { label: 'TS', value: 'ts' }, 18 | { label: 'ES6+', value: 'js' }, 19 | ], 20 | }, 21 | }, 22 | }; 23 | ``` 24 | 25 | JSON Schema format is based on the open source [Formily](https://formilyjs.org/) Schema format. The following are the supported fields: 26 | 27 | ## type 28 | 29 | Defines the type of the current schema. Currently supported types are `string`, `number`, and `object`. `string` type is used for string inputs and dropdown options. `object` type is used for nesting schemas and needs to be used with the `properties` attribute. 30 | 31 | ## title 32 | 33 | Defines the display name of the current schema. 34 | 35 | ## default 36 | 37 | Defines the default value of the current schema. 38 | 39 | ## enum 40 | 41 | Defines the options when the current schema is a dropdown selection. 42 | 43 | The sub-items support `string` or `{ label: string; value: string}` types. When the value and display value are the same in the dropdown options, `string` can be used directly to define the options. 44 | 45 | When representing multiple selection options, set the `default` field to `[]`. 46 | 47 | ## x-validator 48 | 49 | Defines the validation rules for the current schema. When the schema is an input type, validation will be automatically performed after input completion. 50 | 51 | The validation rules supported here are provided by [Formily](https://formilyjs.org/zh-CN/guide/advanced/validate), for example, the maximum value is 5: 52 | 53 | ```js 54 | const schema = { 55 | type: 'object', 56 | properties: { 57 | max_5: { 58 | type: 'number', 59 | title: 'Maximum value (>5 will cause an error)', 60 | 'x-validator': { 61 | maximum: 5, 62 | }, 63 | }, 64 | }, 65 | }; 66 | ``` 67 | 68 | It also supports using validation functions directly: 69 | 70 | ```js 71 | const schema = { 72 | type: 'object', 73 | properties: { 74 | path: { 75 | type: 'string', 76 | title: 'Can only contain numbers and letters', 77 | 'x-validator': value => { 78 | if (!/^[0-9a-zA-Z]*$/g.test(value)) { 79 | return 'Incorrect format'; 80 | } 81 | return ''; 82 | }, 83 | }, 84 | }, 85 | }; 86 | ``` 87 | 88 | ## x-reactions 89 | 90 | Use linkage between schemas. This is exactly the same as [Formily linkage rules](https://formilyjs.org/zh-CN/guide/advanced/linkages). 91 | 92 | For example: 93 | 94 | ```js 95 | const schema = { 96 | type: 'object', 97 | properties: { 98 | name: { 99 | type: 'string', 100 | title: 'Name', 101 | }, 102 | path: { 103 | type: 'string', 104 | title: 'Path', 105 | 'x-reactions': [ 106 | { 107 | dependencies: ['name'], 108 | fulfill: { 109 | state: { 110 | value: '{{$deps[0]}}', 111 | }, 112 | }, 113 | }, 114 | ], 115 | }, 116 | }, 117 | }; 118 | ``` 119 | 120 | ## properties 121 | 122 | Organize the structure of the current schema and define sub-forms. `properties` is an object, where the `key` is the schema keyword and the `value` is a schema object as described above. 123 | -------------------------------------------------------------------------------- /document/en/api/json.md: -------------------------------------------------------------------------------- 1 | # JSON API 2 | 3 | English | [简体中文](../../zh/api/json.md) 4 | 5 | The JSON API is provided by the `@modern-js/codesmith-api-json` package, which provides methods for obtaining JSON file content, updating JSON files, etc. 6 | 7 | ## Usage 8 | 9 | ```ts 10 | import { JsonAPI } from '@modern-js/codesmith-api-json'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const jsonAPI = new JsonAPI(generator); 14 | await jsonAPI.update(context.materials.default.get('package.json'), { 15 | query: {}, 16 | update: { 17 | $set: { 18 | 'dependencies.@modern-js/plugin-bff': `^2.0.0`, 19 | }, 20 | }, 21 | }); 22 | }; 23 | ``` 24 | 25 | - Create an instance of JsonAPI with the generator as the parameter. 26 | - Call the API methods provided on the instance. 27 | 28 | ## API 29 | 30 | ### get 31 | 32 | Get the content of a JSON file. It is defined as follows: 33 | 34 | ```ts 35 | get: (resource: FsResource) => Promise>; 36 | ``` 37 | 38 | - `resource`: Defined by `context.materials.default.get()`. 39 | 40 | ### extend 41 | 42 | Merge an object into a JSON file. It is defined as follows: 43 | 44 | ```ts 45 | extend(resource: FsResource, obj: Record): Promise; 46 | ``` 47 | 48 | - `resource`: Defined by `context.materials.default.get()`. 49 | - `obj`: The object to be merged. 50 | 51 | ### update 52 | 53 | Update the fields of an object into a JSON file. It is defined as follows: 54 | 55 | ```ts 56 | update(resource: FsResource, operation: { 57 | query: Record; 58 | update: Record; 59 | }): Promise; 60 | ``` 61 | 62 | - `resource`: Defined `context.materials.default.get()`. 63 | - `operation`: The update operation, which can be found in detail in [declaration-update](https://www.npmjs.com/package/declaration-update). 64 | -------------------------------------------------------------------------------- /document/en/api/npm.md: -------------------------------------------------------------------------------- 1 | # NPM API 2 | 3 | English | [简体中文](../../zh/api/npm.md) 4 | 5 | The NPM API is provided by the `@modern-js/codesmith-api-npm` package, which provides methods for installing dependencies using different package managers. 6 | 7 | ## Usage 8 | 9 | ```ts 10 | import { NpmAPI } from '@modern-js/codesmith-api-npm'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const npmApi = new NpmAPI(generator); 14 | await npmApi.pnpmInstall({}); 15 | }; 16 | ``` 17 | 18 | - Create an instance of NpmAPI with the same parameters as the micro-generator. 19 | - Call the API methods provided on the instance. 20 | 21 | ## API 22 | 23 | ### npmInstall 24 | 25 | Install dependencies using npm. It is defined as follows: 26 | 27 | ```ts 28 | npmInstall: ({ 29 | cwd, 30 | registryUrl, 31 | ignoreScripts, 32 | }: { 33 | cwd?: string; 34 | registryUrl?: string; 35 | ignoreScripts?: boolean; 36 | useNvm?: boolean; 37 | }) => Promise>; 38 | ``` 39 | 40 | - `cwd`: The directory where the install command is executed, with a default value of `generator.outputPath`. 41 | - `registryUrl`: The registry parameter for installing dependencies. 42 | - `ignoreScripts`: Whether `--ignore-scripts` parameter is needed during dependency installation. 43 | - `useNvm`: Whether to use nvm to switch node versions. 44 | 45 | ### yarnInstall 46 | 47 | Install dependencies using yarn. It is defined as follows: 48 | 49 | ```ts 50 | yarnInstall: ({ 51 | cwd, 52 | registryUrl, 53 | ignoreScripts, 54 | }: { 55 | cwd?: string; 56 | registryUrl?: string; 57 | ignoreScripts?: boolean; 58 | useNvm?: boolean; 59 | }) => Promise>; 60 | ``` 61 | 62 | The parameters are the same as those of `npmInstall`. 63 | 64 | ### pnpmInstall 65 | 66 | Install dependencies using pnpm. It is defined as follows: 67 | 68 | ```ts 69 | pnpmInstall: ({ 70 | cwd, 71 | registryUrl, 72 | ignoreScripts, 73 | }: { 74 | cwd?: string; 75 | registryUrl?: string; 76 | ignoreScripts?: boolean; 77 | useNvm?: boolean; 78 | }) => Promise>; 79 | ``` 80 | 81 | The parameters are the same as those of `npmInstall`. 82 | -------------------------------------------------------------------------------- /document/zh/api/ejs.md: -------------------------------------------------------------------------------- 1 | # EJS API 2 | 3 | [English](../../en/api/ejs.md) | 简体中文 4 | 5 | EJS API 是由 `@modern-js/codesmith-api-ejs` 包提供,该包提供了使用 [EJS](https://ejs.co/) 渲染单个文件及文件夹方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { EjsAPI } from '@modern-js/codesmith-api-ejs'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const ejsAPI = new EjsAPI(generator); 14 | await ejsAPI.renderTemplate( 15 | context.current!.material.get('templates/a.js.ejs'), 16 | 'b.js', 17 | { name: 'test ejs' }, 18 | ); 19 | }; 20 | ``` 21 | 22 | - 创建 EjsAPI 实例,参数为 generator。 23 | - 调用实例上提供的 API 方法。 24 | 25 | ## API 26 | 27 | ### renderTemplate 28 | 29 | 渲染单个 EJS 模板文件。其类型定义为: 30 | 31 | ```ts 32 | renderTemplate: ( 33 | templateResource: FsResource, 34 | target: string, 35 | parameters?: Record, 36 | ) => Promise; 37 | ``` 38 | 39 | - `templateResource`:模板文件,通过 `context.current!.material.get()` 获取。 40 | - `target`: 目标文件路径。 41 | - `parameters`:模板变量值,当模板中存在 EJS 变量时,定义对应变量值。 42 | 43 | ### renderTemplateDir 44 | 45 | 批量渲染 EJS 模板文件夹。其类型定义为: 46 | 47 | ```ts 48 | type TargetFunction = (globMatch: string) => string; 49 | type RenderTemplateDirOptions = { 50 | nodir?: boolean; 51 | dot?: boolean; 52 | ignore?: string | readonly string[]; 53 | parameters?: Record; 54 | }; 55 | renderTemplateDir: ( 56 | material: FsMaterial, 57 | findGlob: string, 58 | target: TargetFunction, 59 | options?: RenderTemplateDirOptions, 60 | ) => Promise; 61 | ``` 62 | 63 | - `material`:当前微生成器文件资源,其值为 `context.current!.material`。 64 | - `findGlob`:模板文件匹配规则,支持 [glob](https://www.npmjs.com/package/glob) 格式,例如:`templates/**/*` 为 templates 目录所有文件。 65 | - `target`:目标文件名称,对满足条件的文件进行重命名,函数参数为文件名称。 66 | - `options.parameters`:模板变量值,当模板中存在 EJS 变量时,定义对应变量值。 67 | - `options.nodir`/`options.dot`/`options.ignore`:glob 匹配参数。 68 | 69 | 示例: 70 | 71 | ```ts 72 | const ejsAPI = new EjsAPI(generator); 73 | await ejsAPI.renderTemplateDir( 74 | context.current!.material, 75 | 'templates/**/*', 76 | (resourceKey: string) => 77 | resourceKey.replace('templates/', '').replace('.ejs', ''), 78 | { parameters: { name: 'name' } }, 79 | ); 80 | ``` 81 | -------------------------------------------------------------------------------- /document/zh/api/fs.md: -------------------------------------------------------------------------------- 1 | # FS API 2 | 3 | [English](../../en/api/fs.md) | 简体中文 4 | 5 | FS API 是由 `@modern-js/codesmith-api-fs` 包提供,该包提供了渲染单个文件及文件夹方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { FsAPI } from '@modern-js/codesmith-api-fs'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const fsAPI = new FsAPI(generator); 14 | await fsAPI.renderFile( 15 | context.current!.material.get('templates/a.txt'), 16 | 'b.txt', 17 | ); 18 | }; 19 | ``` 20 | 21 | - 创建 FsAPI 实例,参数为 generator。 22 | - 调用实例上提供的 API 方法。 23 | 24 | ## API 25 | 26 | ### renderFile 27 | 28 | 渲染单个文件。其类型定义为: 29 | 30 | ```ts 31 | renderFile: (resource: FsResource, target: string) => Promise; 32 | ``` 33 | 34 | - `resource`: `context.current!.material.get()` 获取。 35 | - `target`: 目标文件路径。 36 | 37 | ### renderDir 38 | 39 | 批量渲染文件夹。其类型定义为: 40 | 41 | ```ts 42 | type TargetFunction = (globMatch: string) => string; 43 | type RenderDirOptions = { 44 | nodir?: boolean; 45 | dot?: boolean; 46 | ignore?: string | readonly string[]; 47 | }; 48 | renderDir: ( 49 | material: FsMaterial, 50 | findGlob: string, 51 | target: TargetFunction, 52 | options?: RenderDirOptions, 53 | ) => Promise; 54 | ``` 55 | 56 | - `material`:当前微生成器文件资源,其值为 `context.current!.material`。 57 | - `findGlob`:模板文件匹配规则,支持 [glob](https://www.npmjs.com/package/glob) 格式,例如:`templates/**/*` 为 templates 目录所有文件。 58 | - `target`:目标文件名称,对满足条件的文件进行重命名,函数参数为文件名称。 59 | - `options.nodir`/`options.dot`/`options.ignore`:glob 匹配参数。 60 | 61 | 示例: 62 | 63 | ```ts 64 | const fsAPI = new FsAPI(generator); 65 | await fsAPI.renderDir( 66 | context.current!.material, 67 | 'templates/**/*', 68 | (resourceKey: string) => 69 | resourceKey.replace('templates/', ''), 70 | ); 71 | ``` 72 | -------------------------------------------------------------------------------- /document/zh/api/git.md: -------------------------------------------------------------------------------- 1 | # Git API 2 | 3 | [English](../../en/api/git.md) | 简体中文 4 | 5 | Git API 由 `@modern-js/codesmith-api-git` 包提供,该包提供了初始化 Git 仓库,提交 Git commit 等方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { GitAPI } from '@modern-js/codesmith-api-git'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const gitApi = new GitAPI(generatorCore, generatorContext); 14 | await gitApi.initGitRepo(); 15 | }; 16 | ``` 17 | 18 | - 创建 GitAPI 实例,参数和微生成器参数一致。 19 | - 调用实例上提供的 API 方法。 20 | 21 | ## API 22 | 23 | ### isInGitRepo 24 | 25 | 判断当前项目是否为 Git 仓库。其类型定义为: 26 | 27 | ```ts 28 | isInGitRepo: (cwd?: string) => Promise; 29 | ``` 30 | 31 | - `cwd`:项目目录,默认为 `generator.outputPath`。 32 | 33 | ### initGitRepo 34 | 35 | 初始化当前项目为 Git 仓库。其类型定义为: 36 | 37 | ```ts 38 | initGitRepo(cwd?: string, force?: boolean): Promise; 39 | ``` 40 | 41 | - `cwd`:项目目录,默认为 `generator.outputPath`。 42 | - `force`:如果当前目录已经为一个 Git 仓库,是否强制初始化。 43 | 44 | ### addAndCommit 45 | 46 | 提交当前变更。其类型定义为: 47 | 48 | ```ts 49 | addAndCommit(commitMessage: string, cwd?: string): Promise; 50 | ``` 51 | 52 | - `commitMessage`:commit 信息。 53 | - `cwd`:项目目录,默认为 `generator.outputPath`。 54 | -------------------------------------------------------------------------------- /document/zh/api/handlebars.md: -------------------------------------------------------------------------------- 1 | # Handlebars API 2 | 3 | [English](../../en/api/handlebars.md) | 简体中文 4 | 5 | Handlebars API 是由 `@modern-js/codesmith-api-handlebars` 包提供,该包提供了使用 [Handlebars](https://handlebarsjs.com/) 渲染单个文件及文件夹方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { HandlebarsAPI } from '@modern-js/codesmith-api-handlebars'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const handlebarsAPI = new HandlebarsAPI(generator); 14 | await handlebarsAPI.renderTemplate( 15 | context.current!.material.get('templates/a.js.handlebars'), 16 | 'b.js', 17 | { name: 'test handebars' }, 18 | ); 19 | }; 20 | ``` 21 | 22 | - 创建 HandlebarsAPI 实例,参数为 generator。 23 | - 调用实例上提供的 API 方法。 24 | 25 | ## API 26 | 27 | ### registerHelp 28 | 29 | 注册 Handlebars 助手代码。其类型定义为: 30 | 31 | ```ts 32 | registerHelp: ( 33 | helpers: Record, 34 | ) => Promise; 35 | ``` 36 | 37 | - `helpers`:块助手代码对象,块助手代码的使用可参考[这里](https://handlebarsjs.com/zh/guide/#%E5%9D%97%E5%8A%A9%E6%89%8B%E4%BB%A3%E7%A0%81)。 38 | 39 | ### registerPartials 40 | 41 | 注册 Handlebars 代码片段。其类型定义为: 42 | 43 | ```ts 44 | registerPartials: ( 45 | partials: Record, 46 | ) => Promise; 47 | ``` 48 | 49 | - `partials`:代码片段对象,代码片段的使用可参考[这里](https://handlebarsjs.com/zh/guide/#%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5)。 50 | 51 | ### renderTemplate 52 | 53 | 渲染单个 Handlebars 模板文件。其类型定义为: 54 | 55 | ```ts 56 | renderTemplate: ( 57 | templateResource: FsResource, 58 | target: string, 59 | parameters?: Record, 60 | ) => Promise; 61 | ``` 62 | 63 | - `templateResource`:模板文件,通过 `context.current!.material.get()` 获取。 64 | - `target`: 目标文件路径。 65 | - `parameters`:模板变量值,当模板中存在 Handlebars 变量时,定义对应变量值。 66 | 67 | ### renderTemplateDir 68 | 69 | 批量渲染 Handlebars 模板文件夹。其类型定义为: 70 | 71 | ```ts 72 | type TargetFunction = (globMatch: string) => string; 73 | type RenderTemplateDirOptions = { 74 | nodir?: boolean; 75 | dot?: boolean; 76 | ignore?: string | readonly string[]; 77 | parameters?: Record; 78 | }; 79 | renderTemplateDir: ( 80 | material: FsMaterial, 81 | findGlob: string, 82 | target: TargetFunction, 83 | options?: RenderTemplateDirOptions, 84 | ) => Promise; 85 | ``` 86 | 87 | - `material`:当前微生成器文件资源,其值为 `context.current!.material`。 88 | - `findGlob`:模板文件匹配规则,支持 [glob](https://www.npmjs.com/package/glob) 格式,例如:`templates/**/*` 为 templates 目录所有文件。 89 | - `target`:目标文件名称,对满足条件的文件进行重命名,函数参数为文件名称。 90 | - `options.parameters`:模板变量值,当模板中存在 Handebars 变量时,定义对应变量值。 91 | - `options.nodir`/`options.dot`/`options.ignore`:glob 匹配参数。 92 | 93 | 示例: 94 | 95 | ```ts 96 | const handlebarsAPI = new HandlebarsAPI(generator); 97 | await handlebarsAPI.renderTemplateDir( 98 | context.current!.material, 99 | 'templates/**/*', 100 | (resourceKey: string) => 101 | resourceKey.replace('templates/', '').replace('.handlebars', ''), 102 | { parameters: { name: 'name' } }, 103 | ); 104 | ``` 105 | -------------------------------------------------------------------------------- /document/zh/api/index.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | [English](../../en/api/index.md) | 简体中文 4 | 5 | CodeSmith API 是由不同的 NPM 包提供的,可根据需求安装不同的依赖去使用对应的 API。 6 | 7 | - [App API](./app.md) 8 | 9 | - [Handlebars API](./handlebars.md) 10 | 11 | - [EJS API](./ejs.md) 12 | 13 | - [JSON API](./json.md) 14 | 15 | - [FS API](./fs.md) 16 | 17 | - [Git API](./git.md) 18 | 19 | - [NPM API](./npm.md) 20 | -------------------------------------------------------------------------------- /document/zh/api/input.md: -------------------------------------------------------------------------------- 1 | # Input 2 | 3 | [English](../../en/api/input.md) | 简体中文 4 | 5 | 每个定制化模板都提供了 Input 的方式完成与用户的问题交互,使用 JSON Schema 的方式进行定义: 6 | 7 | 例如: 8 | 9 | ```js 10 | const schema = { 11 | type: 'object', 12 | properties: { 13 | language: { 14 | type: 'string', 15 | title: '开发语言', 16 | enum: [ 17 | { label: 'TS', value: 'ts' }, 18 | { label: 'ES6+', value: 'js' }, 19 | ], 20 | }, 21 | }, 22 | }; 23 | ``` 24 | 25 | JSON Schema 的格式参考了开源的 [Formily](https://formilyjs.org/) Schema 的格式,下面将对支持的字段进行介绍: 26 | 27 | ## type 28 | 29 | 定义当前 Schema 类型,当前支持的类型为 `string`、`number` 和 `object`。字符串输入和下拉选项都需要使用 `string` 类型。 `object` 类型用于实现 Schema 之间嵌套,需要配和 `properties` 属性使用。 30 | 31 | ## title 32 | 33 | 定义当前 Schema 展示名称。 34 | 35 | ## default 36 | 37 | 定义当前 Schema 的默认值。 38 | 39 | ## enum 40 | 41 | 当前 Schema 为下列选项时,定义选项内容。 42 | 43 | 子项支持 `string` 或者 `{ label: string; value: string}` 类型,当下拉选项中值和展示值相同时,可直接使用 `string` 定义。 44 | 45 | 当需要表示多选选项时,设置 `default` 字段为 `[]` 即可。 46 | 47 | ## x-validator 48 | 49 | 当前 Schema 的校验规则。当 Schema 为输入类型时,在输入完成后会自动完成校验。 50 | 51 | 这里校验规则支持[ Formily 提供的校验规则](https://formilyjs.org/zh-CN/guide/advanced/validate),例如最大值为 5: 52 | 53 | ```js 54 | const schema = { 55 | type: 'object', 56 | properties: { 57 | max_5: { 58 | type: 'number', 59 | title: '最大值(>5报错)', 60 | 'x-validator': { 61 | maximum: 5, 62 | }, 63 | }, 64 | }, 65 | }; 66 | ``` 67 | 68 | 也支持直接使用验证函数: 69 | 70 | ```js 71 | const schema = { 72 | type: 'object', 73 | properties: { 74 | path: { 75 | type: 'string', 76 | title: '只能包含数字和字母', 77 | 'x-validator': value => { 78 | if (!/^[0-9a-zA-Z]*$/g.test(value)) { 79 | return '格式不正确'; 80 | } 81 | return ''; 82 | }, 83 | }, 84 | }, 85 | }; 86 | ``` 87 | 88 | ## x-reactions 89 | 90 | 使用 Schema 之间的联动,这里和[ Formily 联动规则](https://formilyjs.org/zh-CN/guide/advanced/linkages)完全相同。 91 | 92 | 例如: 93 | 94 | ```js 95 | const schema = { 96 | type: 'object', 97 | properties: { 98 | name: { 99 | type: 'string', 100 | title: '名称', 101 | }, 102 | path: { 103 | type: 'string', 104 | title: '路径', 105 | 'x-reactions': [ 106 | { 107 | dependencies: ['name'], 108 | fulfill: { 109 | state: { 110 | value: '{{$deps[0]}}', 111 | }, 112 | }, 113 | }, 114 | ], 115 | }, 116 | }, 117 | }; 118 | ``` 119 | 120 | ## properties 121 | 122 | 组织当前 Schema 的结构,定义子表单。`properties` 为对象,`key` 为 Schema 关键字,`value` 为上述描述的 Schema 对象。 123 | -------------------------------------------------------------------------------- /document/zh/api/json.md: -------------------------------------------------------------------------------- 1 | # JSON API 2 | 3 | [English](../../en/api/json.md) | 简体中文 4 | 5 | JSON API 是由 `@modern-js/codesmith-api-json` 包提供,该包提供了获取 JSON 文件内容,更新 JSON 文件等方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { JsonAPI } from '@modern-js/codesmith-api-json'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const jsonAPI = new JsonAPI(generator); 14 | await jsonAPI.update(context.materials.default.get('package.json'), { 15 | query: {}, 16 | update: { 17 | $set: { 18 | 'dependencies.@modern-js/plugin-bff': `^2.0.0`, 19 | }, 20 | }, 21 | }); 22 | }; 23 | ``` 24 | 25 | - 创建 JsonAPI 实例,参数为 generator。 26 | - 调用实例上提供的 API 方法。 27 | 28 | ## API 29 | 30 | ### get 31 | 32 | 获取 JSON 文件内容。其类型定义为: 33 | 34 | ```ts 35 | get: (resource: FsResource) => Promise>; 36 | ``` 37 | 38 | - `resource`: `context.materials.default.get()` 获取。 39 | 40 | ### extend 41 | 42 | 合并对象至 JSON 文件。其类型定义为: 43 | 44 | ```ts 45 | extend(resource: FsResource, obj: Record): Promise; 46 | ``` 47 | 48 | - `resource`: `context.materials.default.get()` 获取。 49 | - `obj`:合并对象 50 | 51 | ### update 52 | 53 | 更新对象字段至 JSON 文件。其类型定义为: 54 | 55 | ```ts 56 | update(resource: FsResource, operation: { 57 | query: Record; 58 | update: Record; 59 | }): Promise; 60 | ``` 61 | 62 | - `resource`: `context.materials.default.get()` 获取。 63 | - `operation`:更新操作,详细使用姿势可查看 [declaration-update](https://www.npmjs.com/package/declaration-update)。 64 | -------------------------------------------------------------------------------- /document/zh/api/npm.md: -------------------------------------------------------------------------------- 1 | # NPM API 2 | 3 | [English](../../en/api/npm.md) | 简体中文 4 | 5 | NPM API 是由 `@modern-js/codesmith-api-npm` 包提供,该包提供了不同的包管理工具安装依赖的方法。 6 | 7 | ## 使用姿势 8 | 9 | ```ts 10 | import { NpmAPI } from '@modern-js/codesmith-api-npm'; 11 | 12 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 13 | const npmApi = new NpmAPI(generator); 14 | await npmApi.pnpmInstall({}); 15 | }; 16 | ``` 17 | 18 | - 创建 NpmAPI 实例,参数和微生成器参数一致。 19 | - 调用实例上提供的 API 方法。 20 | 21 | ## API 22 | 23 | ### npmInstall 24 | 25 | 使用 npm 安装依赖。其类型定义为: 26 | 27 | ```ts 28 | npmInstall: ({ 29 | cwd, 30 | registryUrl, 31 | ignoreScripts, 32 | }: { 33 | cwd?: string; 34 | registryUrl?: string; 35 | ignoreScripts?: boolean; 36 | useNvm?: boolean; 37 | }) => Promise>; 38 | ``` 39 | 40 | - `cwd`: install 命令的执行目录,默认为 `generator.outputPath`。 41 | - `registryUrl`:安装依赖的 registry 参数。 42 | - `ignoreScripts`:安装依赖时是否需要 `--ignore-scripts` 参数。 43 | - `useNvm`:是否使用 nvm 切换 node 版本。 44 | 45 | ### yarnInstall 46 | 47 | 使用 yarn 安装依赖。其类型定义为: 48 | 49 | ```ts 50 | yarnInstall: ({ 51 | cwd, 52 | registryUrl, 53 | ignoreScripts, 54 | }: { 55 | cwd?: string; 56 | registryUrl?: string; 57 | ignoreScripts?: boolean; 58 | useNvm?: boolean; 59 | }) => Promise>; 60 | ``` 61 | 62 | 参数和 `npmInstall` 参数一致。 63 | 64 | ### pnpmInstall 65 | 66 | 使用 pnpm 安装依赖。其类型定义为: 67 | 68 | ```ts 69 | pnpmInstall: ({ 70 | cwd, 71 | registryUrl, 72 | ignoreScripts, 73 | }: { 74 | cwd?: string; 75 | registryUrl?: string; 76 | ignoreScripts?: boolean; 77 | useNvm?: boolean; 78 | }) => Promise>; 79 | ``` 80 | 81 | 参数和 `npmInstall` 参数一致。 82 | -------------------------------------------------------------------------------- /document/zh/start.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | [English](../en/start.md) | 简体中文 4 | 5 | ## 创建项目 6 | 7 | ```bash 8 | mkdir generator-demo && cd generator demo 9 | npx @modern-js/codesmith-cli@latest @modern-js/generator-generator 10 | ? 请填写项目名称 generator-demo 11 | ? 请选择包管理工具 pnpm 12 | ? 请选择开发语言 TS 13 | ``` 14 | 15 | ## 目录结构 16 | 17 | 创建完成后的项目目录结构 18 | 19 | ```bash 20 | . 21 | ├── .changeset 22 | │ └── config.json 23 | ├── .eslintrc.js 24 | ├── .gitignore 25 | ├── .husky 26 | │ └── pre-commit 27 | ├── .npmrc 28 | ├── .nvmrc 29 | ├── .prettierrc 30 | ├── .vscode 31 | │ ├── extensions.json 32 | │ └── settings.json 33 | ├── README.md 34 | ├── modern.config.ts 35 | ├── package.json 36 | ├── src 37 | │ ├── .eslintrc.js 38 | │ ├── index.ts 39 | │ └── modern-app-env.d.ts 40 | ├── templates 41 | └── tsconfig.json 42 | ``` 43 | 44 | 项目是基于 [Modern.js 模块项目](https://modernjs.dev/module-tools) 创建的,核心是下面几个文件: 45 | 46 | ```bash 47 | . 48 | ├── src 49 | │ └── index.ts 50 | ├── templates 51 | │ └── .gitkeep 52 | ``` 53 | 54 | ### src/index.ts 55 | 56 | 该文件用于完成微生成器的内容开发。 57 | 58 | ```ts 59 | import { GeneratorContext, GeneratorCore } from '@modern-js/codesmith'; 60 | import { AppAPI } from '@modern-js/codesmith-api-app'; 61 | 62 | const handleTemplateFile = async (appApi: AppAPI) => { 63 | await appApi.forgeTemplate('templates/**/*'); 64 | }; 65 | 66 | export default async (context: GeneratorContext, generator: GeneratorCore) => { 67 | const appApi = new AppAPI(context, generator); 68 | 69 | const { locale } = context.config; 70 | appApi.i18n.changeLanguage({ locale }); 71 | 72 | if (!(await appApi.checkEnvironment())) { 73 | // eslint-disable-next-line no-process-exit 74 | process.exit(1); 75 | } 76 | 77 | generator.logger.debug(`start run @modern-js/generator-demo`); 78 | generator.logger.debug(`context=${JSON.stringify(context)}`); 79 | generator.logger.debug(`context.data=${JSON.stringify(context.data)}`); 80 | 81 | await handleTemplateFile(appApi); 82 | 83 | await appApi.runInstall(); 84 | 85 | appApi.showSuccessInfo(); 86 | 87 | generator.logger.debug(`forge @modern-js/generator-demo succeed `); 88 | }; 89 | ``` 90 | 91 | 该文件默认导出一个函数,函数参数为 `context` 和 `generator`。`context` 上提供了微生成器运行的一些上下文信息,`generator` 上提供了一些生成器运行的工具和方法。 92 | 93 | ### templates 94 | 95 | `templates` 目录存在当前微生成器的模板文件,支持 Handlebars 和 EJS 格式,微生成器提供了对应操作不同类型文件的 API。 96 | 97 | 如果模板文件为 `js`、`ts` 或者 `json` 文件,推荐直接使用 `.handlebars` 或者 `.ejs` 后缀,可避免项目 eslint 报错和在 Monorepo 中项目识别问题。 98 | 99 | 模板中 `.gitignore` 文件和 `.npmrc` 文件在发布 npm 包时会自动删除,需要使用 `.handlebars` 或者 `.ejs` 后缀将其保留。 100 | 101 | ## 使用 102 | 103 | ### 命令行运行 104 | 105 | CodeSmith 提供了 CLI 工具用于直接运行微生成器,支持两种格式: 106 | 107 | - 绝对路径 108 | 109 | 适用于本地开发调试,开发完成后,在微生成器执行 npm run build 构建项目,然后使用下面命令即可进行测试。 110 | 111 | ```bash 112 | npx @modern-js/codesmith-cli@latest 113 | ``` 114 | 115 | - npm 包 116 | 117 | 适用于微生成器发布于 bnpm 上,共享微生成器场景。 118 | 119 | ```bash 120 | npx @modern-js/codesmith-cli@latest 121 | ``` 122 | 123 | ### 使用 JS 执行 124 | 125 | 除了使用 CLI 的方式执行微生成器,CodeSmith 还支持在代码中执行微生成器。 126 | 127 | - 安装 CodeSmith 依赖 128 | 129 | ```bash 130 | pnpm add @modern-js/codesmith 131 | ``` 132 | 133 | - 创建 CodeSmith 实例 134 | 135 | ```ts 136 | import { CodeSmith } from '@modern-js/codesmith'; 137 | 138 | const smith = new CodeSmith({ 139 | debug: false, 140 | }); 141 | ``` 142 | 143 | > debug 为 true 时将开发 Debug 模式,会打印对应的 debug 日志。 144 | 145 | - 调用 forge 方法运行微生成器 146 | 147 | ```ts 148 | const smith = new CodeSmith({ 149 | debug: false, 150 | }); 151 | 152 | await smith.forge({ 153 | tasks: [ 154 | { 155 | generator: , 156 | config: {}, 157 | }, 158 | ], 159 | pwd: '.', 160 | }); 161 | ``` 162 | -------------------------------------------------------------------------------- /modern.config.js: -------------------------------------------------------------------------------- 1 | import monorepoTools from '@modern-js/monorepo-tools'; 2 | 3 | module.exports = { 4 | plugins: [monorepoTools()], 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "codesmith-monorepo", 4 | "description": "The meta-framework suite designed from scratch for frontend-focused modern web development.", 5 | "homepage": "https://modernjs.dev", 6 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 7 | "repository": "web-infra-dev/codesmith", 8 | "license": "MIT", 9 | "keywords": [ 10 | "react", 11 | "framework", 12 | "modern", 13 | "modern.js" 14 | ], 15 | "scripts": { 16 | "new": "modern new", 17 | "setup": "npm run reset && pnpm install --ignore-scripts", 18 | "reset": "pnpm -r exec rm -rf ./node_modules", 19 | "test": "pnpm run --filter './packages/**' test --detectOpenHandles", 20 | "prepare": "pnpm run --filter './packages/**' prepare", 21 | "lint": "biome check", 22 | "change": "modern change", 23 | "bump": "modern bump", 24 | "pre": "modern pre", 25 | "release": "modern release --ignore-scripts", 26 | "gen-release-note": "modern gen-release-note", 27 | "get-release-version": "cd scripts && pnpm run get-release-version" 28 | }, 29 | "engines": { 30 | "node": ">=14.17.6", 31 | "pnpm": ">=8.0.0" 32 | }, 33 | "packageManager": "pnpm@9.12.2", 34 | "husky": { 35 | "hooks": { 36 | "pre-commit": "lint-staged", 37 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 38 | } 39 | }, 40 | "commitlint": { 41 | "extends": [ 42 | "@commitlint/config-conventional" 43 | ] 44 | }, 45 | "lint-staged": { 46 | "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [ 47 | "biome check --files-ignore-unknown=true" 48 | ] 49 | }, 50 | "workspaces": { 51 | "packages": [ 52 | "apps/*", 53 | "examples/*", 54 | "services/*", 55 | "features/*", 56 | "packages/*", 57 | "packages/api/*", 58 | "packages/easy-form/*" 59 | ] 60 | }, 61 | "devDependencies": { 62 | "@biomejs/biome": "1.8.3", 63 | "@modern-js/monorepo-tools": "2.58.0", 64 | "@modern-js/tsconfig": "2.58.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/api/app/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/app/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/app/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/app/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-app", 3 | "description": "codesmith app api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test --passWithNoTests" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@modern-js/codesmith-api-ejs": "workspace:*", 33 | "@modern-js/codesmith-api-fs": "workspace:*", 34 | "@modern-js/codesmith-api-git": "workspace:*", 35 | "@modern-js/codesmith-api-handlebars": "workspace:*", 36 | "@modern-js/codesmith-api-npm": "workspace:*", 37 | "@modern-js/codesmith-formily": "workspace:*", 38 | "@modern-js/codesmith-utils": "workspace:*", 39 | "@modern-js/plugin-i18n": "2.60.3", 40 | "comment-json": "^4.2.3", 41 | "extra": "^0.2.1", 42 | "inquirer": "8.1.3" 43 | }, 44 | "peerDependencies": { 45 | "@modern-js/codesmith": "workspace:^2.6.8" 46 | }, 47 | "devDependencies": { 48 | "@modern-js/codesmith": "workspace:*", 49 | "@modern-js/module-tools": "2.60.3", 50 | "@modern-js/plugin-testing": "2.60.3", 51 | "@types/inquirer": "^7.3.3", 52 | "@types/jest": "^26.0.24", 53 | "@types/node": "^14.18.42", 54 | "typescript": "^4.9.5" 55 | }, 56 | "sideEffects": false 57 | } 58 | -------------------------------------------------------------------------------- /packages/api/app/src/locale/en.ts: -------------------------------------------------------------------------------- 1 | export const EN_LOCALE = { 2 | environment: { 3 | node_version: 4 | 'The version of Node.js is too low. Please upgrade to the LTS version: https://nodejs.org/', 5 | nvm_install: 'please install nvm first', 6 | yarn_pnpm_npm: 'please install yarn or pnpm or npm first', 7 | }, 8 | install: { 9 | failed: 10 | 'dependencies install failed, please execute `{command}` to install the dependencies ', 11 | failed_no_command: 'dependencies install failed', 12 | success: 'dependencies are automatically installed', 13 | }, 14 | git: { 15 | failed: 16 | 'git repository create failed, please check installing git and setting git username and email', 17 | success: 'git repository has been automatically created', 18 | }, 19 | templated: { 20 | failed: 'forging template failed', 21 | }, 22 | generator: { 23 | failed: 'load sub generator failed', 24 | }, 25 | success: { 26 | info: 'Success!', 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/api/app/src/locale/index.ts: -------------------------------------------------------------------------------- 1 | import { I18n } from '@modern-js/plugin-i18n'; 2 | import { EN_LOCALE } from './en'; 3 | import { ZH_LOCALE } from './zh'; 4 | 5 | const i18n = new I18n(); 6 | 7 | const localeKeys = i18n.init('zh', { zh: ZH_LOCALE, en: EN_LOCALE }); 8 | 9 | export { i18n, localeKeys, I18n }; 10 | -------------------------------------------------------------------------------- /packages/api/app/src/locale/zh.ts: -------------------------------------------------------------------------------- 1 | export const ZH_LOCALE = { 2 | environment: { 3 | node_version: 'Node.js 版本太低,请升级至 LTS 版本: https://nodejs.org/', 4 | nvm_install: '检测到环境中未安装 nvm,请先安装 nvm', 5 | yarn_pnpm_npm: '检测到环境中未安装包管理工具,请先安装 yarn 或 pnpm 或 npm', 6 | }, 7 | install: { 8 | failed: '依赖自动安装失败,请手动执行 `{command}` 命令进行安装', 9 | failed_no_command: '依赖自动安装失败,请手动执行 install 命令进行安装', 10 | success: '依赖自动安装成功', 11 | }, 12 | git: { 13 | failed: 14 | 'git 仓库初始化失败, 请确认是否安装 git 且初始化 git username 和 email', 15 | success: 'git 仓库初始化成功', 16 | }, 17 | templated: { 18 | failed: '模板生成失败', 19 | }, 20 | generator: { 21 | failed: '加载子生成器失败', 22 | }, 23 | success: { 24 | info: '成功!', 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/api/app/src/utils/checkUseNvm.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { type ILogger, fsExists } from '@modern-js/codesmith'; 3 | import { execaWithStreamLog } from '@modern-js/codesmith-api-npm'; 4 | import { execa } from '@modern-js/codesmith-utils/execa'; 5 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 6 | import { canUseFnm, canUseNvm } from '@modern-js/codesmith-utils/npm'; 7 | import { semver } from '@modern-js/codesmith-utils/semver'; 8 | 9 | const NODE_MAJOR_VERSION_MAP: Record = { 10 | 'lts/*': 18, 11 | 'lts/argon': 4, 12 | 'lts/boron': 6, 13 | 'lts/carbon': 8, 14 | 'lts/dubnium': 10, 15 | 'lts/erbium': 12, 16 | 'lts/fermium': 14, 17 | 'lts/gallium': 16, 18 | 'lts/hydrogen': 18, 19 | 'lts/jod': 22, 20 | }; 21 | 22 | export async function getNoteVersion() { 23 | const result = await execa('node', ['--version']); 24 | return result.stdout.slice(1); 25 | } 26 | export async function checkUseNvm(cwd: string, logger: ILogger) { 27 | // check windows 28 | if (process.platform.startsWith('win')) { 29 | return false; 30 | } 31 | // exist .nvmrc file 32 | if (!(await fsExists(path.join(cwd, '.nvmrc')))) { 33 | return false; 34 | } 35 | // check current node version and expect node version 36 | const nvmrcContent = ( 37 | await fs.readFile(path.join(cwd, '.nvmrc'), 'utf-8') 38 | ).replace('\n', ''); 39 | const expectNodeVersion = 40 | NODE_MAJOR_VERSION_MAP[nvmrcContent] || nvmrcContent; 41 | const currentNodeVersion = await getNoteVersion(); 42 | if (expectNodeVersion === semver.major(currentNodeVersion)) { 43 | return false; 44 | } 45 | // check nvm or fnm exist 46 | if (!((await canUseNvm()) || (await canUseFnm()))) { 47 | logger.warn( 48 | `🟡 [Check nvm Error]: Current node version is not expect, you should install ${expectNodeVersion}`, 49 | ); 50 | return false; 51 | } 52 | // run nvm install 53 | try { 54 | await execaWithStreamLog('source ~/.nvm/nvm.sh && nvm install', [], { 55 | shell: true, 56 | cwd, 57 | }); 58 | return true; 59 | } catch (e) { 60 | return false; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/api/app/src/utils/transform.ts: -------------------------------------------------------------------------------- 1 | import { isString } from '@modern-js/codesmith-utils/lodash'; 2 | import type { Question } from 'inquirer'; 3 | 4 | export function transformInquirerSchema( 5 | questions: Question[], 6 | configValue: Record = {}, 7 | validateMap: Record< 8 | string, 9 | ( 10 | input: unknown, 11 | data?: Record, 12 | ) => { success: boolean; error?: string } 13 | > = {}, 14 | initValue: Record = {}, 15 | ) { 16 | for (const question of questions) { 17 | question.default = initValue[question.name!] || question.default; 18 | const originValidate = question.validate; 19 | question.validate = async (input, answers) => { 20 | if (originValidate) { 21 | const result = await originValidate(input, answers); 22 | if (isString(result)) { 23 | return result; 24 | } 25 | } 26 | if (validateMap[question.name!]) { 27 | const result = validateMap[question.name!](input, configValue); 28 | if (result.error) { 29 | return result.error; 30 | } 31 | } 32 | return true; 33 | }; 34 | if (configValue[question.name!]) { 35 | question.when = false; 36 | } 37 | } 38 | return questions; 39 | } 40 | -------------------------------------------------------------------------------- /packages/api/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/ejs/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/ejs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/ejs/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/ejs/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/ejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-ejs", 3 | "description": "codesmith ejs api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "ejs": "^3.1.9" 33 | }, 34 | "peerDependencies": { 35 | "@modern-js/codesmith": "workspace:^2.6.8" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/codesmith": "workspace:*", 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/ejs": "^3.1.2", 42 | "@types/jest": "^26.0.24", 43 | "@types/node": "^14.18.42", 44 | "typescript": "^4.9.5" 45 | }, 46 | "sideEffects": false 47 | } 48 | -------------------------------------------------------------------------------- /packages/api/ejs/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FS_RESOURCE, 3 | type FsMaterial, 4 | type FsResource, 5 | type GeneratorCore, 6 | } from '@modern-js/codesmith'; 7 | import { renderString } from './utils'; 8 | 9 | type TargetFunction = (globMatch: string) => string; 10 | 11 | type RenderTemplateDirOptions = { 12 | nodir?: boolean; 13 | dot?: boolean; 14 | ignore?: string | readonly string[]; 15 | parameters?: Record; 16 | }; 17 | 18 | export { renderString }; 19 | 20 | export class EjsAPI { 21 | protected readonly generatorCore: GeneratorCore; 22 | 23 | constructor(generatorCore: GeneratorCore) { 24 | this.generatorCore = generatorCore; 25 | } 26 | 27 | public async renderTemplate( 28 | templateResource: FsResource, 29 | target: string, 30 | parameters: Record = {}, 31 | ) { 32 | if (templateResource._type !== FS_RESOURCE) { 33 | throw new Error('resource not match'); 34 | } 35 | const resourceValue = await templateResource.value(); 36 | if (typeof resourceValue.content !== 'string') { 37 | throw new Error( 38 | `resource.value is not string, resourceValue=${ 39 | resourceValue as unknown as string 40 | }`, 41 | ); 42 | } 43 | await this.generatorCore.output.fs( 44 | target, 45 | renderString(resourceValue.content, parameters), 46 | { encoding: 'utf-8' }, 47 | ); 48 | } 49 | 50 | public async renderTemplateDir( 51 | material: FsMaterial, 52 | findGlob: string, 53 | target: TargetFunction, 54 | options?: RenderTemplateDirOptions, 55 | ) { 56 | const resourceMap = await material.find(findGlob, { 57 | nodir: true, 58 | ...options, 59 | }); 60 | await Promise.all( 61 | // resourceKey is relate path. example: in `garr-master/package.json`, package.json is resourceKey 62 | Object.keys(resourceMap).map(async resourceKey => { 63 | this.generatorCore.logger.debug( 64 | `💡 [EJS Render Template Dir]: resourceKey=${resourceKey}`, 65 | ); 66 | await this.renderTemplate( 67 | material.get(resourceKey), 68 | target(resourceKey), 69 | options?.parameters, 70 | ); 71 | }), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/api/ejs/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { renderString } from './renderString'; 2 | -------------------------------------------------------------------------------- /packages/api/ejs/src/utils/renderString.ts: -------------------------------------------------------------------------------- 1 | import ejs from 'ejs'; 2 | 3 | export function renderString( 4 | template: string, 5 | fullData: Record, 6 | ): string { 7 | return ejs.render(template, fullData) || ''; 8 | } 9 | -------------------------------------------------------------------------------- /packages/api/ejs/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/api/ejs/tests/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { renderString } from '@/utils'; 2 | 3 | describe('Utils cases', () => { 4 | test('renderString test', () => { 5 | const result = renderString('renderString <%= name %>', { name: 'test' }); 6 | expect(result).toBe('renderString test'); 7 | }); 8 | test('renderString array', () => { 9 | const people = ['geddy', 'neil', 'alex']; 10 | const result = renderString('<%= people.join(", "); %>', { people }); 11 | expect(result).toBe('geddy, neil, alex'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/api/ejs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/fs/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/fs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/fs/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/fs/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/fs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-fs", 3 | "description": "codesmith fs api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test --passWithNoTests" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@modern-js/codesmith-utils": "workspace:*" 33 | }, 34 | "peerDependencies": { 35 | "@modern-js/codesmith": "workspace:^2.6.8" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/codesmith": "workspace:*", 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/jest": "^26.0.24", 42 | "@types/node": "^14.18.42", 43 | "typescript": "^4.9.5" 44 | }, 45 | "sideEffects": false 46 | } 47 | -------------------------------------------------------------------------------- /packages/api/fs/src/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { 3 | FS_RESOURCE, 4 | type FsMaterial, 5 | type FsResource, 6 | type GeneratorCore, 7 | } from '@modern-js/codesmith'; 8 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 9 | 10 | type RenderDirOptions = { 11 | nodir?: boolean; 12 | dot?: boolean; 13 | ignore?: string | readonly string[]; 14 | }; 15 | 16 | type TargetFunction = (globMatch: string) => string; 17 | 18 | export class FsAPI { 19 | protected readonly generatorCore: GeneratorCore; 20 | 21 | constructor(generatorCore: GeneratorCore) { 22 | this.generatorCore = generatorCore; 23 | } 24 | 25 | public async renderFile(resource: FsResource, target: string) { 26 | if (resource._type !== FS_RESOURCE) { 27 | throw new Error('resource not match'); 28 | } 29 | const filePath = path.resolve( 30 | this.generatorCore.outputPath, 31 | target.toString(), 32 | ); 33 | await fs.mkdirp(path.dirname(filePath)); 34 | await fs.copyFile(resource.filePath, filePath); 35 | } 36 | 37 | public async renderDir( 38 | material: FsMaterial, 39 | findGlob: string, 40 | target: TargetFunction, 41 | options?: RenderDirOptions, 42 | ) { 43 | const resourceMap = await material.find(findGlob, { 44 | nodir: true, 45 | ...options, 46 | }); 47 | await Promise.all( 48 | Object.keys(resourceMap).map(async resourceKey => { 49 | this.generatorCore.logger.debug( 50 | `💡 [FS Render Dir]: resourceKey=${resourceKey}`, 51 | ); 52 | await this.renderFile(material.get(resourceKey), target(resourceKey)); 53 | }), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/api/fs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/git/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/git/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/git/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/git/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/git/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-git", 3 | "description": "codesmith git api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@modern-js/codesmith-utils": "workspace:*" 33 | }, 34 | "peerDependencies": { 35 | "@modern-js/codesmith": "workspace:^2.6.8" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/codesmith": "workspace:*", 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/jest": "^26.0.24", 42 | "@types/node": "^14.18.42", 43 | "typescript": "^4.9.5" 44 | }, 45 | "sideEffects": false 46 | } 47 | -------------------------------------------------------------------------------- /packages/api/git/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { GeneratorContext, GeneratorCore } from '@modern-js/codesmith'; 2 | import { 3 | canUseGit, 4 | gitAdd, 5 | gitCommit, 6 | initGitRepo, 7 | isInGitRepo, 8 | } from './utils'; 9 | 10 | export class GitAPI { 11 | protected readonly generatorCore: GeneratorCore; 12 | 13 | protected readonly generatorContext?: GeneratorContext; 14 | 15 | constructor( 16 | generatorCore: GeneratorCore, 17 | generatorContext?: GeneratorContext, 18 | ) { 19 | this.generatorCore = generatorCore; 20 | this.generatorContext = generatorContext; 21 | } 22 | 23 | public async isInGitRepo(cwd: string = this.generatorCore.outputPath) { 24 | const canUse = await canUseGit(); 25 | if (canUse) { 26 | return isInGitRepo(cwd); 27 | } 28 | throw new Error('git is not found'); 29 | } 30 | 31 | public async initGitRepo(cwd = this.generatorCore.outputPath, force = false) { 32 | const canUse = await canUseGit(); 33 | if (!canUse) { 34 | throw new Error('git is not found'); 35 | } 36 | const alreadyInit = await this.isInGitRepo(cwd); 37 | if (alreadyInit && !force) { 38 | this.generatorCore.logger.debug('❗️ [Git Init]: Already init, Skip'); 39 | return; 40 | } 41 | try { 42 | const { 43 | config: { defaultBranch = 'master' }, 44 | } = this.generatorContext || { config: { defaultBranch: 'master' } }; 45 | await initGitRepo(cwd, defaultBranch); 46 | } catch (e) { 47 | this.generatorCore.logger.debug('❗️ [Git Init Error]:', e); 48 | throw e; 49 | } 50 | } 51 | 52 | public async addAndCommit( 53 | commitMessage: string, 54 | cwd = this.generatorCore.outputPath, 55 | ) { 56 | const canUse = await canUseGit(); 57 | if (!canUse) { 58 | throw new Error('git is not found'); 59 | } 60 | try { 61 | await gitAdd(cwd); 62 | await gitCommit(cwd, commitMessage); 63 | } catch (e) { 64 | this.generatorCore.logger.debug('❗️ [Git Add and Commit Error]:', e); 65 | throw e; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/api/git/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { execa } from '@modern-js/codesmith-utils/execa'; 2 | 3 | export async function canUseGit() { 4 | try { 5 | await execa('git', ['--version'], { env: process.env }); 6 | return true; 7 | } catch (e) { 8 | return false; 9 | } 10 | } 11 | 12 | export async function isInGitRepo(cwd: string) { 13 | try { 14 | await execa('git', ['rev-parse', '--is-inside-work-tree'], { 15 | env: process.env, 16 | cwd, 17 | }); 18 | return true; 19 | } catch (e) { 20 | return false; 21 | } 22 | } 23 | 24 | export async function initGitRepo(cwd: string, defaultBranch: string) { 25 | await execa('git', ['init'], { 26 | env: process.env, 27 | cwd, 28 | }); 29 | const { stdout } = await execa('git', ['symbolic-ref', '--short', 'HEAD'], { 30 | env: process.env, 31 | cwd, 32 | }); 33 | 34 | if (stdout !== defaultBranch) { 35 | await execa('git', ['checkout', '-b', defaultBranch], { 36 | env: process.env, 37 | cwd, 38 | }); 39 | } 40 | } 41 | 42 | export async function gitAdd(cwd: string) { 43 | await execa('git', ['add', '-A'], { 44 | env: process.env, 45 | cwd, 46 | }); 47 | } 48 | 49 | export async function gitCommit(cwd: string, commitMessage: string) { 50 | await execa('git', ['commit', '-m', commitMessage, '--no-verify'], { 51 | env: process.env, 52 | cwd, 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /packages/api/git/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | import { 4 | canUseGit, 5 | gitAdd, 6 | gitCommit, 7 | initGitRepo, 8 | isInGitRepo, 9 | } from '@/utils'; 10 | import { execa } from '@modern-js/codesmith-utils/execa'; 11 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 12 | 13 | const cwd = path.join(os.tmpdir(), 'codesmith_test', Math.random().toString()); 14 | 15 | describe('Utils cases', () => { 16 | beforeAll(async () => { 17 | await fs.mkdirp(cwd); 18 | }); 19 | afterAll(async () => { 20 | await fs.remove(cwd); 21 | }); 22 | test('canUseGit test', async () => { 23 | const canUse = await canUseGit(); 24 | expect(canUse).toBe(true); 25 | }); 26 | test('isInGitRepo test', async () => { 27 | const alreadyInit = await isInGitRepo(cwd); 28 | expect(alreadyInit).toBe(false); 29 | }); 30 | test('initGitRepo test', async () => { 31 | await initGitRepo(cwd, 'main'); 32 | const alreadyInit = await isInGitRepo(cwd); 33 | expect(alreadyInit).toBe(true); 34 | }); 35 | test('gitAdd and gitCommit test', async () => { 36 | const packageJson = { 37 | name: 'test-git', 38 | version: '0.1.0', 39 | private: true, 40 | }; 41 | await fs.writeFile( 42 | path.join(cwd, 'package.json'), 43 | JSON.stringify(packageJson), 44 | { encoding: 'utf-8' }, 45 | ); 46 | await gitAdd(cwd); 47 | await gitCommit(cwd, 'feat: init'); 48 | const result = await execa('git', ['log'], { cwd, env: process.env }); 49 | expect(result.stdout.includes('feat: init')).toBeTruthy(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/api/git/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/api/git/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/handlebars/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/handlebars/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/handlebars/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/handlebars/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/handlebars/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-handlebars", 3 | "description": "codesmith handlebars api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "handlebars": "^4.7.7" 33 | }, 34 | "peerDependencies": { 35 | "@modern-js/codesmith": "workspace:^2.6.8" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/codesmith": "workspace:*", 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/jest": "^26.0.24", 42 | "@types/node": "^14.18.42", 43 | "typescript": "^4.9.5" 44 | }, 45 | "sideEffects": false 46 | } 47 | -------------------------------------------------------------------------------- /packages/api/handlebars/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FS_RESOURCE, 3 | type FsMaterial, 4 | type FsResource, 5 | type GeneratorCore, 6 | } from '@modern-js/codesmith'; 7 | import type handlebars from 'handlebars'; 8 | import { renderString } from './utils'; 9 | 10 | type TargetFunction = (globMatch: string) => string; 11 | 12 | type RenderTemplateDirOptions = { 13 | nodir?: boolean; 14 | dot?: boolean; 15 | ignore?: string | readonly string[]; 16 | parameters?: Record; 17 | }; 18 | 19 | export { renderString }; 20 | 21 | export class HandlebarsAPI { 22 | protected readonly generatorCore: GeneratorCore; 23 | 24 | protected readonly registers: { 25 | helpers: Record; 26 | partials: Record; 27 | }; 28 | 29 | constructor( 30 | generatorCore: GeneratorCore, 31 | registers?: { 32 | helpers: Record; 33 | partials: Record; 34 | }, 35 | ) { 36 | this.generatorCore = generatorCore; 37 | this.registers = registers || { helpers: {}, partials: {} }; 38 | } 39 | 40 | public async registerHelp( 41 | helpers: Record, 42 | ) { 43 | this.registers.helpers = { 44 | ...this.registers.helpers, 45 | ...helpers, 46 | }; 47 | } 48 | 49 | public async registerPartials(partials: Record) { 50 | this.registers.partials = { 51 | ...this.registers.partials, 52 | ...partials, 53 | }; 54 | } 55 | 56 | public async renderTemplate( 57 | templateResource: FsResource, 58 | target: string, 59 | parameters: Record = {}, 60 | ) { 61 | if (templateResource._type !== FS_RESOURCE) { 62 | throw new Error('resource not match'); 63 | } 64 | const resourceValue = await templateResource.value(); 65 | if (typeof resourceValue.content !== 'string') { 66 | throw new Error( 67 | `resource.value is not string, resourceValue=${ 68 | resourceValue as unknown as string 69 | }`, 70 | ); 71 | } 72 | await this.generatorCore.output.fs( 73 | target, 74 | renderString(resourceValue.content, parameters, this.registers), 75 | { encoding: 'utf-8' }, 76 | ); 77 | } 78 | 79 | public async renderTemplateDir( 80 | material: FsMaterial, 81 | findGlob: string, 82 | target: TargetFunction, 83 | options?: RenderTemplateDirOptions, 84 | ) { 85 | const resourceMap = await material.find(findGlob, { 86 | nodir: true, 87 | ...options, 88 | }); 89 | await Promise.all( 90 | // resourceKey is relate path. example: in `garr-master/package.json`, package.json is resourceKey 91 | Object.keys(resourceMap).map(async resourceKey => { 92 | this.generatorCore.logger.debug( 93 | `💡 [Handlebars Render Template Dir]: resourceKey=${resourceKey}`, 94 | ); 95 | await this.renderTemplate( 96 | material.get(resourceKey), 97 | target(resourceKey), 98 | options?.parameters, 99 | ); 100 | }), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/api/handlebars/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { renderString } from './renderString'; 2 | -------------------------------------------------------------------------------- /packages/api/handlebars/src/utils/renderString.ts: -------------------------------------------------------------------------------- 1 | import handlebars from 'handlebars'; 2 | 3 | export function renderString( 4 | template: string, 5 | fullData: Record, 6 | registers?: { 7 | helpers: Record; 8 | partials: Record; 9 | }, 10 | ): string { 11 | const helpers: Record = { 12 | ...registers?.helpers, 13 | }; 14 | const partials: Record = { 15 | ...registers?.partials, 16 | }; 17 | Object.keys(helpers).forEach(h => handlebars.registerHelper(h, helpers[h])); 18 | Object.keys(partials).forEach(p => 19 | handlebars.registerPartial(p, partials[p]), 20 | ); 21 | return handlebars.compile(template)(fullData) || ''; 22 | } 23 | -------------------------------------------------------------------------------- /packages/api/handlebars/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/api/handlebars/tests/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { renderString } from '@/utils'; 2 | 3 | describe('Utils cases', () => { 4 | test('renderString test', () => { 5 | const result = renderString('renderString {{ name }}', { name: 'test' }); 6 | expect(result).toBe('renderString test'); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/handlebars/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/json/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/json/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/json/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/json/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-json", 3 | "description": "codesmith json api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test --passWithNoTests" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@modern-js/codesmith": "workspace:*", 33 | "comment-json": "^4.2.3", 34 | "declaration-update": "^0.0.2" 35 | }, 36 | "devDependencies": { 37 | "@modern-js/module-tools": "2.60.3", 38 | "@modern-js/plugin-testing": "2.60.3", 39 | "@types/jest": "^26.0.24", 40 | "@types/node": "^14.18.42", 41 | "typescript": "^4.9.5" 42 | }, 43 | "sideEffects": false 44 | } 45 | -------------------------------------------------------------------------------- /packages/api/json/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { FsResource, GeneratorCore } from '@modern-js/codesmith'; 2 | import commentJSON from 'comment-json'; 3 | import * as declarationUpdate from 'declaration-update'; 4 | import { editJson } from './utils'; 5 | 6 | export class JsonAPI { 7 | protected readonly generatorCore: GeneratorCore; 8 | 9 | constructor(generatorCore: GeneratorCore) { 10 | this.generatorCore = generatorCore; 11 | } 12 | 13 | public async get(resource: FsResource) { 14 | const originJsonValue = await resource.value(); 15 | try { 16 | const origin = commentJSON.parse(originJsonValue.content as string); 17 | return origin; 18 | } catch (e) { 19 | this.generatorCore.logger.debug('❗️ [JSON Get Parse Error]:', e); 20 | throw new Error('resource content is not a legal json'); 21 | } 22 | } 23 | 24 | public async extend( 25 | resource: FsResource, 26 | obj: Record, 27 | endWithNewLine = false, 28 | ) { 29 | await editJson(this.generatorCore, resource, async () => { 30 | const originJsonValue = await resource.value(); 31 | try { 32 | const origin = commentJSON.parse(originJsonValue.content as string); 33 | const newObj = commentJSON.assign(origin, obj); 34 | const jsonIntent = 2; 35 | return ( 36 | commentJSON.stringify(newObj, undefined, jsonIntent) + 37 | (endWithNewLine ? '\n' : '') 38 | ); 39 | } catch (e) { 40 | this.generatorCore.logger.debug('❗️ [JSON Extend Parse Error]:', e); 41 | throw new Error('resource content is not a legal json'); 42 | } 43 | }); 44 | } 45 | 46 | public async update( 47 | resource: FsResource, 48 | operation: { query: Record; update: Record }, 49 | endWithNewLine = false, 50 | ) { 51 | await editJson(this.generatorCore, resource, text => { 52 | try { 53 | const jsonContent = commentJSON.parse(text) as Record; 54 | declarationUpdate.query(jsonContent, operation.query, operation.update); 55 | const jsonIntent = 2; 56 | return Promise.resolve( 57 | commentJSON.stringify(jsonContent, undefined, jsonIntent) + 58 | (endWithNewLine ? '\n' : ''), 59 | ); 60 | } catch (e) { 61 | this.generatorCore.logger.debug('❗️ [JSON Update Parse Error]:', e); 62 | throw new Error('resource content is not a legal json'); 63 | } 64 | }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/api/json/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import type { FsResource, GeneratorCore } from '@modern-js/codesmith'; 2 | 3 | export async function editJson( 4 | generatorCore: GeneratorCore, 5 | resource: FsResource, 6 | getNewJsonValue: (text: string) => Promise, 7 | ): Promise { 8 | const originJsonValue = await resource.value(); 9 | const newJsonString = await getNewJsonValue( 10 | originJsonValue.content as string, 11 | ); 12 | if (!newJsonString) { 13 | throw new Error('get new json string is undefined'); 14 | } 15 | await generatorCore.output.fs(resource.filePath, newJsonString, { 16 | encoding: 'utf-8', 17 | }); 18 | return newJsonString; 19 | } 20 | -------------------------------------------------------------------------------- /packages/api/json/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import commentJSON from 'comment-json'; 2 | import * as declarationUpdate from 'declaration-update'; 3 | 4 | describe('update json', () => { 5 | it('simple string', () => { 6 | const jsonContent = commentJSON.parse('{}'); 7 | declarationUpdate.query( 8 | jsonContent, 9 | {}, 10 | { 11 | $set: { a: '1' }, 12 | }, 13 | ); 14 | expect(jsonContent).toEqual({ a: '1' }); 15 | }); 16 | it('complex string', () => { 17 | const jsonContent = commentJSON.parse('{}'); 18 | declarationUpdate.query( 19 | jsonContent, 20 | {}, 21 | { 22 | $set: { 23 | a: { 24 | b: '2', 25 | }, 26 | }, 27 | }, 28 | ); 29 | expect(jsonContent).toEqual({ 30 | a: { 31 | b: '2', 32 | }, 33 | }); 34 | }); 35 | it('simple array', () => { 36 | const jsonContent = commentJSON.parse('{}'); 37 | declarationUpdate.query( 38 | jsonContent, 39 | {}, 40 | { 41 | $set: ['1', '2', '3'], 42 | }, 43 | ); 44 | expect(jsonContent).toEqual({ 45 | '0': '1', 46 | '1': '2', 47 | '2': '3', 48 | }); 49 | }); 50 | it('complex array', () => { 51 | const jsonContent = commentJSON.parse('{}'); 52 | declarationUpdate.query( 53 | jsonContent, 54 | {}, 55 | { 56 | $set: [ 57 | { 58 | a: '1', 59 | }, 60 | { 61 | b: '2', 62 | }, 63 | { 64 | c: '3', 65 | }, 66 | ], 67 | }, 68 | ); 69 | expect(jsonContent).toEqual({ 70 | '0': { 71 | a: '1', 72 | }, 73 | '1': { 74 | b: '2', 75 | }, 76 | '2': { 77 | c: '3', 78 | }, 79 | }); 80 | }); 81 | it('string and array', () => { 82 | const jsonContent = commentJSON.parse('{}'); 83 | declarationUpdate.query( 84 | jsonContent, 85 | {}, 86 | { 87 | $set: { 88 | a: ['1', '2', '3'], 89 | b: { 90 | b1: 'b1', 91 | }, 92 | }, 93 | }, 94 | ); 95 | expect(jsonContent).toEqual({ 96 | a: ['1', '2', '3'], 97 | b: { 98 | b1: 'b1', 99 | }, 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /packages/api/json/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/api/json/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/api/npm/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/api/npm/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/api/npm/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/api/npm/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/api/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-api-npm", 3 | "description": "codesmith npm api", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@modern-js/codesmith-utils": "workspace:*" 33 | }, 34 | "peerDependencies": { 35 | "@modern-js/codesmith": "workspace:^2.6.8" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/codesmith": "workspace:*", 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/jest": "^26.0.24", 42 | "@types/node": "^14.18.42", 43 | "typescript": "^4.9.5" 44 | }, 45 | "sideEffects": false 46 | } 47 | -------------------------------------------------------------------------------- /packages/api/npm/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { GeneratorCore } from '@modern-js/codesmith'; 2 | import type { ExecaReturnValue } from '@modern-js/codesmith-utils/execa'; 3 | import { npmInstall, pnpmInstall, yarnInstall } from './utils'; 4 | 5 | export * from './utils'; 6 | export class NpmAPI { 7 | protected readonly generatorCore: GeneratorCore; 8 | 9 | constructor(generatorCore: GeneratorCore) { 10 | this.generatorCore = generatorCore; 11 | } 12 | 13 | public npmInstall({ 14 | cwd, 15 | registryUrl, 16 | ignoreScripts, 17 | }: { 18 | cwd?: string; 19 | registryUrl?: string; 20 | ignoreScripts?: boolean; 21 | useNvm?: boolean; 22 | }): Promise { 23 | return npmInstall({ 24 | cwd: cwd || this.generatorCore.outputPath, 25 | registryUrl, 26 | ignoreScripts, 27 | }); 28 | } 29 | 30 | public yarnInstall({ 31 | cwd, 32 | registryUrl, 33 | ignoreScripts, 34 | }: { 35 | cwd?: string; 36 | registryUrl?: string; 37 | ignoreScripts?: boolean; 38 | useNvm?: boolean; 39 | }): Promise { 40 | return yarnInstall({ 41 | cwd: cwd || this.generatorCore.outputPath, 42 | registryUrl, 43 | ignoreScripts, 44 | }); 45 | } 46 | 47 | public pnpmInstall({ 48 | cwd, 49 | registryUrl, 50 | ignoreScripts, 51 | }: { 52 | cwd?: string; 53 | registryUrl?: string; 54 | ignoreScripts?: boolean; 55 | useNvm?: boolean; 56 | }): Promise { 57 | return pnpmInstall({ 58 | cwd: cwd || this.generatorCore.outputPath, 59 | registryUrl, 60 | ignoreScripts, 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/api/npm/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | canUseNvm, 3 | canUseFnm, 4 | canUseNpm, 5 | canUseYarn, 6 | canUsePnpm, 7 | } from '@modern-js/codesmith-utils/npm'; 8 | export { 9 | npmInstall, 10 | yarnInstall, 11 | pnpmInstall, 12 | execaWithStreamLog, 13 | runInstallWithNvm, 14 | } from './install'; 15 | -------------------------------------------------------------------------------- /packages/api/npm/src/utils/install.ts: -------------------------------------------------------------------------------- 1 | import { type ExecaReturnValue, execa } from '@modern-js/codesmith-utils/execa'; 2 | import { 3 | canUseNpm, 4 | canUsePnpm, 5 | canUseYarn, 6 | } from '@modern-js/codesmith-utils/npm'; 7 | 8 | export function execaWithStreamLog( 9 | command: string, 10 | args: string[], 11 | options: Record, 12 | ) { 13 | const promise = execa(command, args, { 14 | ...options, 15 | stdin: 'inherit', 16 | stdout: 'inherit', 17 | stderr: 'inherit', 18 | }); 19 | return promise; 20 | } 21 | 22 | export async function runInstallWithNvm( 23 | command: string, 24 | options: Record, 25 | ): Promise { 26 | return await execa(`~/.nvm/nvm-exec ${command}`, { 27 | ...options, 28 | shell: true, 29 | stdin: 'inherit', 30 | stdout: 'inherit', 31 | stderr: 'inherit', 32 | }); 33 | } 34 | 35 | export async function npmInstall({ 36 | cwd, 37 | registryUrl, 38 | ignoreScripts, 39 | useNvm, 40 | }: { 41 | cwd: string; 42 | registryUrl?: string; 43 | ignoreScripts?: boolean; 44 | useNvm?: boolean; 45 | }): Promise { 46 | const canUse = await canUseNpm(); 47 | if (canUse) { 48 | const params = ['install']; 49 | if (registryUrl) { 50 | params.push(`--registry=${registryUrl}`); 51 | } 52 | if (ignoreScripts) { 53 | params.push('--ignore-scripts'); 54 | } 55 | if (useNvm) { 56 | return runInstallWithNvm(`npm ${params.join(' ')}`, { 57 | cwd, 58 | env: process.env, 59 | }); 60 | } 61 | return execaWithStreamLog('npm', params, { 62 | cwd, 63 | env: process.env, 64 | }); 65 | } 66 | throw new Error('please install npm first'); 67 | } 68 | 69 | export async function yarnInstall({ 70 | cwd, 71 | registryUrl, 72 | ignoreScripts, 73 | useNvm, 74 | }: { 75 | cwd: string; 76 | registryUrl?: string; 77 | ignoreScripts?: boolean; 78 | useNvm?: boolean; 79 | }): Promise { 80 | const canUse = await canUseYarn(); 81 | if (canUse) { 82 | const params = ['install']; 83 | if (registryUrl) { 84 | params.push(`--registry=${registryUrl}`); 85 | } 86 | if (ignoreScripts) { 87 | params.push('--ignore-scripts'); 88 | } 89 | if (useNvm) { 90 | return runInstallWithNvm(`yarn ${params.join(' ')}`, { 91 | cwd, 92 | env: process.env, 93 | }); 94 | } 95 | return execaWithStreamLog('yarn', params, { cwd, env: process.env }); 96 | } 97 | throw new Error('please install yarn first'); 98 | } 99 | 100 | export async function pnpmInstall({ 101 | cwd, 102 | registryUrl, 103 | ignoreScripts, 104 | useNvm, 105 | }: { 106 | cwd: string; 107 | registryUrl?: string; 108 | ignoreScripts?: boolean; 109 | useNvm?: boolean; 110 | }): Promise { 111 | const canUse = await canUsePnpm(); 112 | if (canUse) { 113 | const params = ['install']; 114 | if (registryUrl) { 115 | params.push(`--registry=${registryUrl}`); 116 | } 117 | if (ignoreScripts) { 118 | params.push('--ignore-scripts'); 119 | } 120 | if (useNvm) { 121 | return runInstallWithNvm(`yarn ${params.join(' ')}`, { 122 | cwd, 123 | env: process.env, 124 | }); 125 | } 126 | return execaWithStreamLog('pnpm', params, { cwd, env: process.env }); 127 | } 128 | throw new Error('please install pnpm first'); 129 | } 130 | -------------------------------------------------------------------------------- /packages/api/npm/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/api/npm/tests/utils.test.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | import { 4 | execaWithStreamLog, 5 | npmInstall, 6 | pnpmInstall, 7 | yarnInstall, 8 | } from '@/utils/install'; 9 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 10 | import { 11 | canUseFnm, 12 | canUseNpm, 13 | canUseNvm, 14 | canUseYarn, 15 | } from '@modern-js/codesmith-utils/npm'; 16 | 17 | describe('Env utils cases', () => { 18 | test('can use nvm', async () => { 19 | const result = await canUseNvm(); 20 | expect(typeof result === 'boolean').toBeTruthy(); 21 | }); 22 | test('can use fnm', async () => { 23 | const result = await canUseFnm(); 24 | expect(typeof result === 'boolean').toBeTruthy(); 25 | }); 26 | test('can use npm', async () => { 27 | const result = await canUseNpm(); 28 | expect(typeof result === 'boolean').toBeTruthy(); 29 | }); 30 | test('can use yarn', async () => { 31 | const result = await canUseYarn(); 32 | expect(typeof result === 'boolean').toBeTruthy(); 33 | }); 34 | test('can use yarn', async () => { 35 | const result = await canUseYarn(); 36 | expect(typeof result === 'boolean').toBeTruthy(); 37 | }); 38 | }); 39 | 40 | const cwd = path.join(os.tmpdir(), 'codesmith_test', Math.random().toString()); 41 | 42 | describe('Install cases', () => { 43 | beforeEach(async () => { 44 | await fs.mkdirp(cwd); 45 | const packageJson = { 46 | name: 'test-npm', 47 | version: '0.1.0', 48 | private: true, 49 | dependencies: { 50 | lodash: '^4', 51 | }, 52 | }; 53 | await fs.writeFile( 54 | path.join(cwd, 'package.json'), 55 | JSON.stringify(packageJson), 56 | { encoding: 'utf-8' }, 57 | ); 58 | }); 59 | afterEach(async () => { 60 | await fs.remove(cwd); 61 | }); 62 | test('npm install', async () => { 63 | const result = await npmInstall({ cwd }); 64 | expect(result?.exitCode).toBe(0); 65 | expect(fs.existsSync(path.join(cwd, 'node_modules', 'lodash'))).toBe(true); 66 | }); 67 | test('yarn install', async () => { 68 | if (!(await canUseYarn())) { 69 | await execaWithStreamLog('npm', ['install', '-g', 'yarn'], {}); 70 | } 71 | const result = await yarnInstall({ cwd }); 72 | expect(result?.exitCode).toBe(0); 73 | expect(fs.existsSync(path.join(cwd, 'node_modules', 'lodash'))).toBe(true); 74 | }); 75 | test('pnpm install', async () => { 76 | const result = await pnpmInstall({ cwd }); 77 | expect(result?.exitCode).toBe(0); 78 | expect(fs.existsSync(path.join(cwd, 'node_modules', 'lodash'))).toBe(true); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /packages/api/npm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/cli/bin/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | const pkgInfo = require(path.join(__dirname, '../package.json')); 7 | const srcPath = pkgInfo['jsnext:source']; 8 | const distPath = pkgInfo.main; 9 | const project = path.join(__dirname, '../tsconfig.json'); 10 | let env = 'production'; 11 | if (fs.existsSync(project)) { 12 | env = 'development'; 13 | } 14 | if (process.env.NODE_ENV) { 15 | env = process.env.NODE_ENV; 16 | } 17 | 18 | if (env === 'development') { 19 | require('ts-node').register({ 20 | project, 21 | transpileOnly: true, 22 | typeCheck: false, 23 | }); 24 | } 25 | 26 | try { 27 | require(`../${env === 'development' ? srcPath : distPath}`).default(); 28 | } catch (e) { 29 | console.error(e); 30 | } 31 | -------------------------------------------------------------------------------- /packages/cli/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildConfig: { 7 | autoExternal: false, 8 | dts: false, 9 | sideEffects: false, 10 | }, 11 | plugins: [moduleTools(), testingPlugin()], 12 | }); 13 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-cli", 3 | "description": "codesmith cli tools", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "main": "./dist/index.js", 17 | "bin": { 18 | "codesmith": "./bin/run.js" 19 | }, 20 | "files": [ 21 | "/bin", 22 | "/dist" 23 | ], 24 | "publishConfig": { 25 | "registry": "https://registry.npmjs.org/", 26 | "access": "public" 27 | }, 28 | "scripts": { 29 | "prepare": "pnpm build", 30 | "new": "modern new", 31 | "build": "modern build", 32 | "test": "modern test --passWithNoTests" 33 | }, 34 | "dependencies": { 35 | "commander": "10.0.0", 36 | "@swc/helpers": "0.5.1", 37 | "@modern-js/codesmith": "workspace:*", 38 | "@modern-js/codesmith-utils": "workspace:*", 39 | "@modern-js/plugin-i18n": "2.60.3" 40 | }, 41 | "devDependencies": { 42 | "@modern-js/module-tools": "2.60.3", 43 | "@modern-js/plugin-testing": "2.60.3", 44 | "@types/jest": "^26.0.24", 45 | "@types/node": "^14.18.42", 46 | "ts-node": "^10.9.1", 47 | "typescript": "^4.9.5" 48 | }, 49 | "sideEffects": false 50 | } 51 | -------------------------------------------------------------------------------- /packages/cli/src/actions/genAction.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { CodeSmith } from '@modern-js/codesmith'; 3 | import { getLocalLanguage } from '../utils'; 4 | 5 | interface LocalOptions { 6 | debug?: boolean; 7 | time?: boolean; 8 | config: string; 9 | registry?: string; 10 | pwd?: string; 11 | } 12 | 13 | export async function genAction(generator: string, genOptions: LocalOptions) { 14 | const { debug, time, config, registry, pwd } = genOptions; 15 | const smith = new CodeSmith({ 16 | debug, 17 | time, 18 | registryUrl: registry, 19 | }); 20 | 21 | await smith.prepareGlobal(); 22 | 23 | smith.logger.debug('💡 [Runtime Gen Action]'); 24 | smith.logger.debug('💡 [Generator Name]:', generator); 25 | smith.logger.debug('💡 [Generator Pwd]:', pwd); 26 | smith.logger.debug('💡 [Generator Debug]:', debug); 27 | smith.logger.debug('💡 [Generator Options]:', config); 28 | 29 | let runPwd = process.cwd(); 30 | if (pwd) { 31 | if (path.isAbsolute(pwd)) { 32 | runPwd = pwd; 33 | smith.logger.debug('💡 [PWD is Absolute Path]:', pwd); 34 | } else { 35 | runPwd = path.join(process.cwd(), pwd); 36 | smith.logger.debug('💡 [PWD is Relative Path]:', pwd); 37 | } 38 | } 39 | 40 | let targetConfig: Record = {}; 41 | try { 42 | targetConfig = JSON.parse(config); 43 | } catch (e) { 44 | smith.logger.error('🔴 [Config Parse Error]:', e); 45 | return; 46 | } 47 | 48 | const tasks = [ 49 | { 50 | name: generator, 51 | config: { 52 | locale: getLocalLanguage(), 53 | ...targetConfig, 54 | }, 55 | }, 56 | ]; 57 | 58 | await smith.forge({ 59 | tasks: tasks.map(runner => ({ 60 | generator: runner.name, 61 | config: runner.config, 62 | })), 63 | pwd: runPwd, 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /packages/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Command } from 'commander'; 2 | import { genAction } from './actions/genAction'; 3 | 4 | export default function () { 5 | const program = new Command(); 6 | 7 | program 8 | .command('gen ', { isDefault: true }) 9 | .description('use csmith to generator something') 10 | .option('-d,--debug', 'using debug mode to log something', false) 11 | .option('--time', 'show time logger', false) 12 | .option('-p,--pwd ', 'process working directory', process.cwd()) 13 | .option('--config ', 'config for this generator(json string)', '{}') 14 | .option( 15 | '--registry ', 16 | 'set npm registry url to run npm command', 17 | undefined, 18 | ) 19 | .action(genAction); 20 | 21 | program.parse(process.argv); 22 | } 23 | -------------------------------------------------------------------------------- /packages/cli/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { I18CLILanguageDetector } from '@modern-js/plugin-i18n/language-detector'; 2 | 3 | export function getLocalLanguage() { 4 | const detector = new I18CLILanguageDetector(); 5 | return detector.detect(); 6 | } 7 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | modern.config.* 41 | tsconfig.json 42 | CHANGELOG.md 43 | -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/core/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith", 3 | "description": "codesmith core implement", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test --env=node" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "axios": "^1.6.0", 33 | "debug": "4.3.7", 34 | "tar": "^6.1.13", 35 | "@modern-js/codesmith-utils": "workspace:*" 36 | }, 37 | "devDependencies": { 38 | "@modern-js/module-tools": "2.60.3", 39 | "@modern-js/plugin-testing": "2.60.3", 40 | "@types/jest": "^26.0.24", 41 | "@types/node": "^14.18.42", 42 | "@types/tar": "^4.0.5", 43 | "@types/debug": "^4.1.12", 44 | "typescript": "^4.9.5" 45 | }, 46 | "sideEffects": false 47 | } 48 | -------------------------------------------------------------------------------- /packages/core/src/codesmith/constants.ts: -------------------------------------------------------------------------------- 1 | export interface ForgeTask { 2 | // generator name, support 'local generator' and 'npm generator' 3 | generator: string; 4 | // generator config 5 | config?: Record; 6 | } 7 | 8 | export interface ForgeOptions { 9 | tasks: ForgeTask[]; 10 | pwd?: string; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/codesmith/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { GeneratorCore } from '@/generator'; 3 | import { Logger } from '@/logger'; 4 | import { LoggerLevel } from '@/logger/constants'; 5 | import { MaterialsManager } from '@/materials'; 6 | import { FsMaterial } from '@/materials/FsMaterial'; 7 | import type { ForgeOptions, ForgeTask } from './constants'; 8 | 9 | interface ICreateOptions { 10 | debug?: boolean; 11 | time?: boolean; 12 | logger?: Logger; 13 | // custom npm registry 14 | registryUrl?: string; 15 | namespace?: string; 16 | } 17 | 18 | export class CodeSmith { 19 | core?: GeneratorCore; 20 | 21 | materialsManager: MaterialsManager; 22 | 23 | // current mode is debug mode 24 | debug = false; 25 | 26 | logger: Logger; 27 | 28 | constructor({ debug, time, logger, registryUrl, namespace }: ICreateOptions) { 29 | this.debug = debug || false; 30 | this.logger = 31 | logger || 32 | new Logger( 33 | debug 34 | ? LoggerLevel.Debug 35 | : time 36 | ? LoggerLevel.Timing 37 | : LoggerLevel.Info, 38 | namespace, 39 | ); 40 | this.materialsManager = new MaterialsManager(this.logger, registryUrl); 41 | } 42 | 43 | public async forge({ tasks, pwd }: ForgeOptions) { 44 | this.logger?.timing?.('🕒 Codesmith Task'); 45 | this.core = new GeneratorCore({ 46 | logger: this.logger, 47 | materialsManager: this.materialsManager, 48 | outputPath: pwd || process.cwd(), 49 | }); 50 | this.core.addMaterial( 51 | 'default', 52 | new FsMaterial(path.resolve(pwd || process.cwd())), 53 | ); 54 | try { 55 | for (const task of tasks) { 56 | await this.runTask(task); 57 | } 58 | } catch (e: unknown) { 59 | this.logger.error('🔴 [Run Forge Generator Error]:', e); 60 | throw new Error('run task error'); 61 | } finally { 62 | this.logger?.timing?.('🕒 Codesmith Task', true); 63 | } 64 | } 65 | 66 | private async runTask(task: ForgeTask) { 67 | if (!this.core) { 68 | throw new Error("no core in 'runTask'"); 69 | } 70 | const { generator, config } = task; 71 | this.logger?.timing?.(`🕒 RunTask ${generator}`); 72 | await this.core.runGenerator(generator, config); 73 | this.logger?.timing?.(`🕒 RunTask ${generator}`, true); 74 | } 75 | 76 | public async prepareGenerators(generators: string[]) { 77 | await this.materialsManager.prepareGenerators(generators); 78 | } 79 | 80 | public async prepareGlobal() { 81 | if ((global as any).CODESMITH_PREPARE_GLOBAL) { 82 | return; 83 | } 84 | await this.materialsManager.prepareGlobal(); 85 | (global as any).CODESMITH_PREPARE_GLOBAL = true; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/core/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const NPM_API_TIMEOUT = 30000; 2 | export const CATCHE_VALIDITY_PREIOD = 7 * 24 * 3600 * 1000; 3 | -------------------------------------------------------------------------------- /packages/core/src/generator/constants.ts: -------------------------------------------------------------------------------- 1 | import type { FsMaterial } from '@/materials/FsMaterial'; 2 | 3 | export interface RuntimeCurrent { 4 | material: FsMaterial; 5 | } 6 | 7 | export interface GeneratorContext { 8 | materials: Record; 9 | config: Record; // Generator config 10 | data?: Record; // RuntimeCurrent context data 11 | current: RuntimeCurrent | null; // RuntimeCurrent 12 | [key: string]: any; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export type { ILogger } from './logger/constants'; 2 | export { LoggerLevel } from './logger/constants'; 3 | export { Logger } from './logger'; 4 | 5 | export { CodeSmith } from './codesmith'; 6 | export { GeneratorCore } from './generator'; 7 | export type { GeneratorContext } from './generator/constants'; 8 | 9 | export { MaterialsManager } from './materials'; 10 | export { FsMaterial } from './materials/FsMaterial'; 11 | export { FsResource, FS_RESOURCE } from './materials/FsResource'; 12 | 13 | export * from './utils'; 14 | -------------------------------------------------------------------------------- /packages/core/src/logger/constants.ts: -------------------------------------------------------------------------------- 1 | export enum LoggerLevel { 2 | Error = 'error', 3 | Warn = 'warn', 4 | Info = 'info', 5 | Debug = 'debug', 6 | Verbose = 'verbose', 7 | Stream = 'stream', 8 | Timing = 'timing', 9 | } 10 | 11 | type LeveledLogMethod = (...meta: any[]) => void; 12 | 13 | type TimingMethod = (key: string, end?: boolean) => void; 14 | 15 | export interface ILogger { 16 | level: LoggerLevel; 17 | // for cli and npm levels 18 | [LoggerLevel.Error]: LeveledLogMethod; 19 | [LoggerLevel.Warn]: LeveledLogMethod; 20 | [LoggerLevel.Info]: LeveledLogMethod; 21 | [LoggerLevel.Timing]: TimingMethod; 22 | [LoggerLevel.Debug]: LeveledLogMethod; 23 | [LoggerLevel.Verbose]: LeveledLogMethod; 24 | [LoggerLevel.Stream]: LeveledLogMethod; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/logger/index.ts: -------------------------------------------------------------------------------- 1 | import { chalk } from '@modern-js/codesmith-utils/chalk'; 2 | import { type Debugger, debug } from 'debug'; 3 | import { type ILogger, LoggerLevel } from './constants'; 4 | 5 | export { LoggerLevel }; 6 | export class Logger implements ILogger { 7 | level: LoggerLevel = LoggerLevel.Info; 8 | errorLogger: Debugger; 9 | warningLogger: Debugger; 10 | debugLogger: Debugger; 11 | timingLogger: Debugger; 12 | verboseLogger: Debugger; 13 | streamLogger: Debugger; 14 | 15 | constructor(level: LoggerLevel = LoggerLevel.Info, namespace = 'codesmith') { 16 | if (!process.env.DEBUG) { 17 | process.env.DEBUG = `${namespace}:*`; 18 | debug.enable(process.env.DEBUG); 19 | } 20 | this.level = level; 21 | this.warningLogger = debug(`${namespace}:warn`); 22 | this.errorLogger = debug(`${namespace}:error`); 23 | this.debugLogger = debug(`${namespace}:debug`); 24 | this.timingLogger = debug(`${namespace}:timing`); 25 | this.verboseLogger = debug(`${namespace}:verbose`); 26 | this.streamLogger = debug(`${namespace}:stream`); 27 | } 28 | 29 | info(...meta: any[]) { 30 | console.log(chalk.green('[INFO]'), ...meta); 31 | } 32 | 33 | error(message: string, ...args: any[]) { 34 | this.errorLogger(message, ...args); 35 | } 36 | 37 | warn(message: string, ...args: any[]) { 38 | this.warningLogger(message, ...args); 39 | } 40 | 41 | debug(message: string, ...args: any[]) { 42 | if (this.level === LoggerLevel.Timing) { 43 | return; 44 | } 45 | if (this.level === LoggerLevel.Debug) { 46 | this.debugLogger(message, ...args); 47 | } 48 | } 49 | 50 | timing(key: string, end?: boolean) { 51 | if (this.level !== LoggerLevel.Timing) { 52 | return; 53 | } 54 | if (end) { 55 | this.timingLogger(`[Time End] ${key}`); 56 | console.timeEnd(key); 57 | } else { 58 | this.timingLogger(`[Time Start] ${key}`); 59 | console.time(key); 60 | } 61 | } 62 | 63 | verbose(message: string, ...args: any[]) { 64 | if (this.level === LoggerLevel.Verbose) { 65 | this.verboseLogger(message, ...args); 66 | } 67 | } 68 | 69 | stream(message: string, ...args: any[]) { 70 | if (this.level === LoggerLevel.Stream) { 71 | this.streamLogger(message, ...args); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /packages/core/src/materials/FsMaterial.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { glob } from '@modern-js/codesmith-utils/glob'; 3 | import { FsResource } from './FsResource'; 4 | 5 | const promisifyGlob = ( 6 | pattern: string, 7 | options: glob.IOptions, 8 | ): Promise => 9 | new Promise((resolve, reject) => { 10 | glob(pattern, options, (err, files) => 11 | err === null ? resolve(files) : reject(err), 12 | ); 13 | }); 14 | 15 | export class FsMaterial { 16 | basePath: string; 17 | 18 | constructor(basePath: string) { 19 | this.basePath = basePath; 20 | } 21 | 22 | get(resourceKey: string) { 23 | return new FsResource( 24 | path.resolve(this.basePath, resourceKey), 25 | resourceKey, 26 | ); 27 | } 28 | 29 | async find( 30 | globStr: string, 31 | options?: { 32 | nodir?: boolean; 33 | dot?: boolean; 34 | ignore?: string | readonly string[]; 35 | }, 36 | ): Promise> { 37 | const matches = await promisifyGlob(globStr, { 38 | cwd: path.resolve(this.basePath), 39 | nodir: options?.nodir, 40 | dot: options?.dot, 41 | ignore: options?.ignore, 42 | }); 43 | return matches.reduce>((pre, cur) => { 44 | pre[cur] = new FsResource(path.resolve(this.basePath, cur), cur); 45 | return pre; 46 | }, {}); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/core/src/materials/FsResource.ts: -------------------------------------------------------------------------------- 1 | import type { Buffer } from 'buffer'; 2 | import path from 'path'; 3 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 4 | import { IMAGE_EXT_LIST } from './constants'; 5 | 6 | export const FS_RESOURCE = '_codesmith_core_fs_resource'; 7 | export class FsResource { 8 | _type: string = FS_RESOURCE; 9 | 10 | filePath: string; 11 | 12 | resourceKey: string; 13 | 14 | constructor(filePath: string, resourceKey: string) { 15 | this.filePath = filePath; 16 | this.resourceKey = resourceKey; 17 | } 18 | 19 | async value(): Promise<{ content: string | Buffer }> { 20 | const resourceFileExt = path.extname(this.filePath); 21 | if (IMAGE_EXT_LIST.includes(resourceFileExt)) { 22 | const buffer = await fs.readFile(path.resolve(this.filePath)); 23 | return { content: buffer }; 24 | } 25 | const text = await fs.readFile(path.resolve(this.filePath), 'utf8'); 26 | return { content: text }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/materials/constants.ts: -------------------------------------------------------------------------------- 1 | export const IMAGE_EXT_LIST = [ 2 | '.jpg', 3 | '.jpeg', 4 | '.png', 5 | '.gif', 6 | '.bmp', 7 | '.ico', 8 | '.icon', 9 | '.mpt', 10 | '.psd', 11 | '.wmf', 12 | ]; 13 | -------------------------------------------------------------------------------- /packages/core/src/materials/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import type { Logger } from '@/logger'; 3 | import { 4 | downloadPackage, 5 | getGeneratorVersion, 6 | getPackageInfo, 7 | nodeRequire, 8 | } from '@/utils'; 9 | import { FsMaterial } from './FsMaterial'; 10 | 11 | export class MaterialsManager { 12 | logger?: Logger; 13 | 14 | registryUrl?: string; 15 | 16 | materialMap: { 17 | [materialUri: string]: FsMaterial; 18 | }; 19 | 20 | readyGenerators = new Set(); 21 | 22 | constructor(logger?: Logger, registryUrl?: string) { 23 | this.logger = logger; 24 | this.registryUrl = registryUrl; 25 | this.materialMap = {}; 26 | } 27 | 28 | loadLocalGenerator(generator: string): Promise { 29 | if (!path.isAbsolute(generator)) { 30 | return Promise.reject(new Error('only support absolute local path')); 31 | } 32 | const fsMaterial = new FsMaterial(generator); 33 | this.materialMap[generator] = fsMaterial; 34 | return Promise.resolve(fsMaterial); 35 | } 36 | 37 | async loadRemoteGenerator(generator: string): Promise { 38 | const { name, version } = getPackageInfo(generator); 39 | const localPath = await downloadPackage(name, version, { 40 | registryUrl: this.registryUrl, 41 | install: true, 42 | logger: this.logger, 43 | }); 44 | const pkgJson = nodeRequire(`${localPath}/package.json`); 45 | const materialKey = `${pkgJson.name}@${pkgJson.version}`; 46 | this.materialMap[materialKey] = new FsMaterial(localPath); 47 | return Promise.resolve(this.materialMap[materialKey]); 48 | } 49 | 50 | async prepareGenerators(generators: string[]) { 51 | this.logger?.timing?.('🕒 Prepare Generators'); 52 | await Promise.all( 53 | generators.map(async generator => { 54 | if (this.readyGenerators.has(generator)) { 55 | return Promise.resolve(); 56 | } 57 | const { name, version: pkgVersion } = getPackageInfo(generator); 58 | const version = await getGeneratorVersion(name, pkgVersion, { 59 | registryUrl: this.registryUrl, 60 | logger: this.logger, 61 | }); 62 | const materialKey = `${name}@${version}`; 63 | if (this.materialMap[materialKey] || generator.startsWith('file:')) { 64 | return Promise.resolve(); 65 | } 66 | await this.loadRemoteGenerator(materialKey); 67 | this.readyGenerators.add(generator); 68 | }), 69 | ); 70 | this.logger?.timing?.('🕒 Prepare Generators', true); 71 | } 72 | 73 | async prepareGlobal() { 74 | this.logger?.timing?.('🕒 Prepare Global'); 75 | const globalPkgName = '@modern-js/codesmith-global'; 76 | const globalResource = await this.loadRemoteGenerator( 77 | `${globalPkgName}@latest`, 78 | ); 79 | require(globalResource.basePath); 80 | this.logger?.timing?.('🕒 Prepare Global', true); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/core/src/utils/fsExists.ts: -------------------------------------------------------------------------------- 1 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 2 | 3 | /** 4 | * when the path can read successfully, the function will return true 5 | * @param {string} path 6 | * @returns {boolean} 7 | */ 8 | export async function fsExists(path: string) { 9 | try { 10 | await fs.access(path); 11 | return true; 12 | } catch (e: unknown) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/utils/getGeneratorDir.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 3 | import { fsExists } from './fsExists'; 4 | 5 | const MaxTimes = 5; 6 | 7 | export async function getGeneratorDir(generator: string) { 8 | let result = generator; 9 | const isDirectory = (await fs.stat(generator)).isDirectory(); 10 | 11 | if (!isDirectory) { 12 | result = path.dirname(generator); 13 | } 14 | 15 | let times = 0; 16 | 17 | while ( 18 | times < MaxTimes && 19 | !(await fsExists(path.join(result, 'package.json'))) 20 | ) { 21 | result = path.join(result, '../'); 22 | times++; 23 | } 24 | 25 | if (times >= MaxTimes) { 26 | throw Error('generator is not valid'); 27 | } 28 | 29 | return result; 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/utils/getNpmPackageInfo.ts: -------------------------------------------------------------------------------- 1 | import { NPM_API_TIMEOUT } from '@/constants'; 2 | import { execa } from '@modern-js/codesmith-utils/execa'; 3 | import axios from 'axios'; 4 | import type { ILogger } from '../logger/constants'; 5 | import { getNpmRegistry } from './getNpmRegistry'; 6 | import { timeoutPromise } from './timeoutPromise'; 7 | 8 | interface Options { 9 | registryUrl?: string; 10 | logger?: ILogger; 11 | } 12 | 13 | interface PackageInfo { 14 | version: string; 15 | dist: { 16 | tarball: string; 17 | }; 18 | } 19 | 20 | const NpmPackageInfoCache = new Map(); 21 | 22 | async function getNpmPackageInfoWithCommand( 23 | pkgName: string, 24 | pkgVersion: string, 25 | options?: Options, 26 | ): Promise { 27 | const { registryUrl } = options || {}; 28 | const params = [ 29 | 'view', 30 | `${pkgName}@${pkgVersion}`, 31 | 'dist', 32 | 'version', 33 | '--json', 34 | ]; 35 | 36 | if (registryUrl) { 37 | params.push('--registry'); 38 | params.push(registryUrl); 39 | } 40 | 41 | const getPkgInfoPromise = execa('npm', params); 42 | const { stdout } = await timeoutPromise( 43 | getPkgInfoPromise, 44 | NPM_API_TIMEOUT, 45 | `Get npm tarball of '${pkgName}'`, 46 | ); 47 | 48 | try { 49 | const pkgDistInfo = JSON.parse(stdout); 50 | return pkgDistInfo; 51 | } catch (e) { 52 | throw new Error( 53 | `Version \`${pkgVersion}\` for package \`${pkgName}\` could not be found`, 54 | ); 55 | } 56 | } 57 | 58 | export async function getNpmPackageInfo( 59 | pkgName: string, 60 | pkgVersion: string, 61 | options?: Options, 62 | ): Promise { 63 | const packageName = `${pkgName}@${pkgVersion}`; 64 | const packageInfo = NpmPackageInfoCache.get(packageName); 65 | if (packageInfo) { 66 | return packageInfo; 67 | } 68 | const { registryUrl = await getNpmRegistry(), logger } = options || {}; 69 | 70 | const url = `${registryUrl.replace(/\/$/, '')}/${pkgName}/${pkgVersion || 'latest'}`; 71 | 72 | let response: PackageInfo; 73 | try { 74 | response = ( 75 | await timeoutPromise( 76 | axios.get(url), 77 | NPM_API_TIMEOUT, 78 | `Get npm package info of '${pkgName}'`, 79 | ) 80 | ).data; 81 | // some registry not return version field, use command to compat 82 | if (!response.version) { 83 | response = await getNpmPackageInfoWithCommand( 84 | pkgName, 85 | pkgVersion, 86 | options, 87 | ); 88 | } 89 | } catch (e) { 90 | logger?.error(e); 91 | response = await getNpmPackageInfoWithCommand(pkgName, pkgVersion, options); 92 | } 93 | 94 | const { version } = response; 95 | 96 | NpmPackageInfoCache.set(`${pkgName}@${version || pkgVersion}`, response); 97 | 98 | return response; 99 | } 100 | -------------------------------------------------------------------------------- /packages/core/src/utils/getNpmRegistry.ts: -------------------------------------------------------------------------------- 1 | import { execa } from '@modern-js/codesmith-utils/execa'; 2 | 3 | export async function getNpmRegistry() { 4 | try { 5 | const { stdout } = await execa('npm', [ 6 | 'config', 7 | 'get', 8 | 'registry', 9 | '--no-workspaces', 10 | ]); 11 | return stdout.replace(/\/$/, '') || 'https://registry.npmjs.org'; 12 | } catch (error) { 13 | // If getting registry fails, return default npm registry 14 | return 'https://registry.npmjs.org'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/utils/getNpmTarballUrl.ts: -------------------------------------------------------------------------------- 1 | import type { ILogger } from '../logger/constants'; 2 | import { getNpmPackageInfo } from './getNpmPackageInfo'; 3 | 4 | interface Options { 5 | registryUrl?: string; 6 | logger?: ILogger; 7 | } 8 | 9 | export async function getNpmTarballUrl( 10 | pkgName: string, 11 | pkgVersion: string, 12 | options?: Options, 13 | ): Promise { 14 | const packageInfo = await getNpmPackageInfo(pkgName, pkgVersion, options); 15 | 16 | return packageInfo.dist.tarball; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/utils/getNpmVersion.ts: -------------------------------------------------------------------------------- 1 | import type { ILogger } from '../logger/constants'; 2 | import { getNpmPackageInfo } from './getNpmPackageInfo'; 3 | /** 4 | * get package version 5 | * @param {string} packageName 6 | * @param {Options} options 7 | * @returns {string} 8 | */ 9 | 10 | interface Options { 11 | registryUrl?: string; 12 | version?: string; 13 | logger?: ILogger; 14 | } 15 | 16 | export async function getNpmVersion( 17 | packageName: string, 18 | options?: Options, 19 | ): Promise { 20 | const { version = 'latest' } = options || {}; 21 | const packageInfo = await getNpmPackageInfo(packageName, version, options); 22 | 23 | return packageInfo.version; 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/utils/getPackageInfo.ts: -------------------------------------------------------------------------------- 1 | import { semver } from '@modern-js/codesmith-utils/semver'; 2 | 3 | /** 4 | * get package name and package version from package name string 5 | * @param {string} packageName 6 | * @returns {name: string, version: string} 7 | */ 8 | export function getPackageInfo(packageName: string) { 9 | if (!packageName) { 10 | throw new Error('package is not exisit'); 11 | } 12 | const splitAt = packageName.split('@'); 13 | let pkgVersion = 'latest'; 14 | let pkgName = packageName; 15 | if ( 16 | (!packageName.startsWith('@') && splitAt.length === 2) || 17 | (packageName.startsWith('@') && splitAt.length === 3) 18 | ) { 19 | const semverValid = semver.valid(splitAt[splitAt.length - 1]); 20 | if (semverValid === null) { 21 | pkgVersion = splitAt[splitAt.length - 1]; 22 | pkgName = packageName.slice(0, packageName.lastIndexOf('@')); 23 | } else { 24 | pkgVersion = semverValid; 25 | pkgName = packageName.split(semverValid)[0].slice(0, -1); 26 | } 27 | } 28 | return { 29 | name: pkgName, 30 | version: pkgVersion, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { fsExists } from './fsExists'; 2 | export { nodeRequire } from './nodeRequire'; 3 | export { canUsePnpm, canUseYarn, runInstall } from './packageManager'; 4 | export { timeoutPromise } from './timeoutPromise'; 5 | export { downloadPackage, getGeneratorVersion } from './downloadPackage'; 6 | export { getNpmVersion } from './getNpmVersion'; 7 | export { getPackageInfo } from './getPackageInfo'; 8 | export { getNpmRegistry } from './getNpmRegistry'; 9 | export { getNpmPackageInfo } from './getNpmPackageInfo'; 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/nodeRequire.ts: -------------------------------------------------------------------------------- 1 | declare function __non_webpack_require__(path: string): any; 2 | 3 | /** 4 | * requier function support webpack require 5 | * @param {string} path 6 | * @returns {unknown} 7 | */ 8 | export const nodeRequire = (path: string) => { 9 | try { 10 | const module = __non_webpack_require__(path); 11 | if (module?.default) { 12 | return module.default; 13 | } 14 | return module; 15 | } catch (error) { 16 | const module = require(path); 17 | if (module?.default) { 18 | return module.default; 19 | } 20 | return module; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /packages/core/src/utils/packageManager.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import type { Logger } from '@/logger'; 3 | import { execa } from '@modern-js/codesmith-utils/execa'; 4 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 5 | 6 | export async function canUseYarn() { 7 | try { 8 | await execa('yarn', ['--version'], { 9 | env: process.env, 10 | }); 11 | return true; 12 | } catch (e) { 13 | return false; 14 | } 15 | } 16 | 17 | export async function canUsePnpm() { 18 | try { 19 | await execa('pnpm', ['--version'], { 20 | env: process.env, 21 | }); 22 | return true; 23 | } catch (e) { 24 | return false; 25 | } 26 | } 27 | 28 | export async function runInstall( 29 | targetDir: string, 30 | registryUrl?: string, 31 | logger?: Logger, 32 | ) { 33 | const options = { 34 | cwd: targetDir, 35 | env: process.env, 36 | }; 37 | // delete devDependencies 38 | try { 39 | const pkgPath = path.join(targetDir, 'package.json'); 40 | const pkgJSON = JSON.parse(fs.readFileSync(pkgPath, { encoding: 'utf-8' })); 41 | pkgJSON.devDependencies = undefined; 42 | fs.writeFileSync(pkgPath, JSON.stringify(pkgJSON, null, 2), { 43 | encoding: 'utf-8', 44 | }); 45 | } catch (e) { 46 | /** 47 | * no handle 48 | */ 49 | } 50 | 51 | const showLog = logger?.level === 'debug'; 52 | 53 | if (await canUsePnpm()) { 54 | const params = [ 55 | 'install', 56 | '--prod', 57 | showLog ? null : '--reporter=silent', // if debug mode, console install log 58 | '--ignore-scripts', 59 | ].filter(Boolean) as string[]; 60 | if (registryUrl) { 61 | params.push(`--registry=${registryUrl}`); 62 | } 63 | await execa('pnpm', params, options); 64 | } else if (await canUseYarn()) { 65 | const params = [ 66 | 'install', 67 | '--production', 68 | showLog ? null : '--silent', 69 | '--ignore-scripts', 70 | ].filter(Boolean) as string[]; 71 | if (registryUrl) { 72 | params.push(`--registry=${registryUrl}`); 73 | } 74 | await execa('yarn', params, options); 75 | } else { 76 | const params = [ 77 | 'install', 78 | '--production', 79 | '--loglevel=error', 80 | '--ignore-scripts', 81 | ]; 82 | if (registryUrl) { 83 | params.push(`--registry=${registryUrl}`); 84 | } 85 | await execa('npm', params, options); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/core/src/utils/timeoutPromise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * when promise is timeput, reject promise 3 | * @param {Promise} promise 4 | * @param {number} ms 5 | * @param {string} reason 6 | * @returns {Promise} 7 | */ 8 | export async function timeoutPromise( 9 | promise: Promise, 10 | ms: number, 11 | reason = 'Operation', 12 | ) { 13 | let timeoutId: NodeJS.Timeout | null = null; 14 | const delayPromise = (ms: number) => 15 | new Promise(resolve => { 16 | timeoutId = setTimeout(resolve, ms); 17 | }); 18 | const timeout = delayPromise(ms).then(() => { 19 | throw new Error(`${reason} timed out after ${ms}ms`); 20 | }); 21 | try { 22 | const result = await Promise.race([promise, timeout]); 23 | return result; 24 | } finally { 25 | if (timeoutId) { 26 | clearTimeout(timeoutId); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/tests/utils/downloadPackage.test.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | import { downloadPackage } from '@/utils'; 4 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 5 | 6 | jest.setTimeout(100000); 7 | 8 | describe('downloadPackage function test', () => { 9 | beforeEach(() => { 10 | fs.removeSync(path.join(os.tmpdir(), 'csmith-generator')); 11 | }); 12 | it.skip('download lodash package', async () => { 13 | const packageDir = await downloadPackage('lodash'); 14 | expect(packageDir).toContain('csmith-generator/lodash'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/core/tests/utils/fsExists.test.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | import { fsExists } from '@/utils'; 4 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 5 | 6 | describe('fsExists function test', () => { 7 | beforeEach(() => { 8 | const tmpDir = os.tmpdir(); 9 | fs.removeSync(path.join(tmpDir, '_codesmith_test')); 10 | }); 11 | 12 | it('file exists', async () => { 13 | const tmpDir = os.tmpdir(); 14 | const fileName = 'tmp.txt'; 15 | const filePath = path.join(tmpDir, '_codesmith_test', fileName); 16 | await fs.createFile(filePath); 17 | const exists = await fsExists(filePath); 18 | expect(exists).toBe(true); 19 | }); 20 | it('file not exists', async () => { 21 | const tmpDir = os.tmpdir(); 22 | const fileName = 'tmp_not_exit.txt'; 23 | const filePath = path.join(tmpDir, '_codesmith_test', fileName); 24 | const exists = await fsExists(filePath); 25 | expect(exists).toBe(false); 26 | }); 27 | afterAll(() => { 28 | const tmpDir = os.tmpdir(); 29 | const dirPath = path.join(tmpDir, '_codesmith_test'); 30 | fs.remove(dirPath); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/core/tests/utils/getGeneratorDir.test.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import * as path from 'path'; 3 | import { fsExists } from '@/utils'; 4 | import { getGeneratorDir } from '@/utils/getGeneratorDir'; 5 | import { fs } from '@modern-js/codesmith-utils/fs-extra'; 6 | 7 | describe('getGeneratorDir function test', () => { 8 | it('normal', async () => { 9 | const tmpDir = path.join(os.tmpdir(), '__codesmith__test', 'generatorDir'); 10 | const generarorPath = path.join(tmpDir, 'path1', 'path2'); 11 | const file = path.join(tmpDir, 'package.json'); 12 | if (await fsExists(tmpDir)) { 13 | fs.removeSync(tmpDir); 14 | } 15 | fs.mkdirpSync(generarorPath); 16 | fs.writeFile(file, '{}', 'utf-8'); 17 | const generarorDir = await getGeneratorDir(generarorPath); 18 | const lastChar = generarorDir[generarorDir.length - 1]; 19 | expect( 20 | lastChar === path.sep ? generarorDir.slice(0, -1) : generarorDir, 21 | ).toBe(tmpDir); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/core/tests/utils/getNpmVersion.test.ts: -------------------------------------------------------------------------------- 1 | import { getNpmVersion } from '@/utils'; 2 | 3 | describe.skip('getNpmVersion function test', () => { 4 | it('package exists', async () => { 5 | const version = await getNpmVersion('lodash'); 6 | expect(typeof version).toBe('string'); 7 | }); 8 | it('package not exists', async () => { 9 | try { 10 | await getNpmVersion('lodash_xx'); 11 | } catch (e: any) { 12 | expect(e.message).toContain(`lodash_xx@latest' is not in this registry`); 13 | } 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/core/tests/utils/getNpmtarballUrl.test.ts: -------------------------------------------------------------------------------- 1 | import { getNpmTarballUrl } from '@/utils'; 2 | 3 | describe.skip('getNpmTarballUrl function test', () => { 4 | it('package exists', async () => { 5 | const tarball = await getNpmTarballUrl('lodash', '4.17.21'); 6 | expect(tarball).toContain('lodash-4.17.21.tgz'); 7 | }); 8 | it('package not exists', async () => { 9 | try { 10 | await getNpmTarballUrl('lodash', '4.1.2'); 11 | } catch (e: any) { 12 | expect(e.message).toContain( 13 | 'Version `4.1.2` for package `lodash` could not be found', 14 | ); 15 | } 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/core/tests/utils/getPackageInfo.test.ts: -------------------------------------------------------------------------------- 1 | import { getPackageInfo } from '@/utils'; 2 | 3 | describe('getPackageInfo function test', () => { 4 | it('package not start with @', async () => { 5 | const packageInfo = await getPackageInfo('lodash@4.17.2'); 6 | expect(packageInfo).toEqual({ 7 | name: 'lodash', 8 | version: '4.17.2', 9 | }); 10 | }); 11 | it('package start with @', async () => { 12 | const packageInfo = await getPackageInfo('@babel/runtime@^7.14.8'); 13 | expect(packageInfo).toEqual({ 14 | name: '@babel/runtime', 15 | version: '^7.14.8', 16 | }); 17 | }); 18 | it('package start with @, but not contains version', async () => { 19 | const packageInfo = await getPackageInfo('@babel/runtime'); 20 | expect(packageInfo).toEqual({ 21 | name: '@babel/runtime', 22 | version: 'latest', 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/formily/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/formily/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/formily/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/formily/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/formily/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-formily", 3 | "description": "codesmith support formly schema to inquirer question", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "@formily/json-schema": "^2.2.24", 33 | "@formily/validator": "^2.2.24", 34 | "@modern-js/codesmith-utils": "workspace:*", 35 | "inquirer": "^8.2.5" 36 | }, 37 | "peerDependencies": { 38 | "@modern-js/codesmith": "workspace:^2.6.8" 39 | }, 40 | "devDependencies": { 41 | "@modern-js/codesmith": "workspace:*", 42 | "@modern-js/module-tools": "2.60.3", 43 | "@modern-js/plugin-testing": "2.60.3", 44 | "@types/inquirer": "^7.3.3", 45 | "@types/jest": "^26.0.24", 46 | "@types/node": "^14.18.42", 47 | "typescript": "^4.9.5" 48 | }, 49 | "sideEffects": false 50 | } 51 | -------------------------------------------------------------------------------- /packages/formily/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { GeneratorContext } from '@modern-js/codesmith'; 2 | import { merge } from '@modern-js/codesmith-utils/lodash'; 3 | import { CLIReader } from './inquirer'; 4 | import type { Schema } from './transform'; 5 | 6 | export * from './inquirer'; 7 | export type { Schema } from './transform'; 8 | export type { SchemaEnum } from '@formily/json-schema'; 9 | export { validate } from '@formily/validator'; 10 | 11 | export class FormilyAPI { 12 | protected readonly generatorContext: GeneratorContext; 13 | 14 | constructor(generatorContext: GeneratorContext) { 15 | this.generatorContext = generatorContext; 16 | } 17 | 18 | private mergeAnswers( 19 | answers: Record, 20 | configValue: Record, 21 | ) { 22 | const inputData = merge(answers, configValue); 23 | this.generatorContext.config = merge( 24 | this.generatorContext.config, 25 | inputData, 26 | ); 27 | return inputData; 28 | } 29 | 30 | public async getInputBySchemaFunc( 31 | schemaFunc: (config?: Record) => Schema, 32 | configValue: Record = {}, 33 | validateMap: Record< 34 | string, 35 | ( 36 | input: unknown, 37 | data?: Record, 38 | ) => { success: boolean; error?: string } 39 | > = {}, 40 | initValue: Record = {}, 41 | ) { 42 | const reader = new CLIReader({ 43 | schema: schemaFunc(configValue), 44 | validateMap, 45 | initValue, 46 | }); 47 | reader.setAnswers(configValue); 48 | const answers = await reader.start(); 49 | return this.mergeAnswers(answers, configValue); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/formily/src/inquirer.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from './prompt'; 2 | import type { Schema } from './transform'; 3 | 4 | export interface ICLIReaderOptions { 5 | schema: Schema; 6 | validateMap?: Record< 7 | string, 8 | ( 9 | input: unknown, 10 | data?: Record, 11 | ) => { success: boolean; error?: string } 12 | >; 13 | initValue?: Record; 14 | } 15 | export class CLIReader< 16 | T extends Record = Record, 17 | > { 18 | schema: Schema | null = null; 19 | 20 | validateMap: Record< 21 | string, 22 | ( 23 | input: unknown, 24 | data?: Record, 25 | ) => { success: boolean; error?: string } 26 | >; 27 | 28 | initValue: Record; 29 | 30 | answers: Record = {}; 31 | 32 | constructor(options: ICLIReaderOptions) { 33 | const { schema, validateMap, initValue } = options; 34 | this.schema = schema; 35 | this.validateMap = validateMap || {}; 36 | this.initValue = initValue || {}; 37 | } 38 | 39 | getAnswers() { 40 | return this.answers as T; 41 | } 42 | 43 | setAnswers(answers: Partial) { 44 | this.answers = { ...this.answers, ...answers }; 45 | } 46 | 47 | async start() { 48 | if (!this.schema) { 49 | throw Error('schema is not valid'); 50 | } 51 | const answers = await prompt( 52 | this.schema, 53 | this.answers, 54 | this.validateMap, 55 | this.initValue, 56 | ); 57 | this.setAnswers(answers); 58 | return answers; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/formily/tests/prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { prompt } from '../src/prompt'; 2 | import type { Schema } from '../src/transform'; 3 | 4 | describe('prompt test', () => { 5 | it('has config value', async () => { 6 | const schema: Schema = { 7 | type: 'object', 8 | properties: { 9 | language: { 10 | type: 'string', 11 | title: '开发语言', 12 | default: 'ts', 13 | }, 14 | }, 15 | }; 16 | const ans = await prompt(schema, { language: 'ts' }, {}, {}); 17 | expect(ans).toEqual({ language: 'ts' }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/formily/tests/transform.test.ts: -------------------------------------------------------------------------------- 1 | import { type Question, type Schema, transformForm } from '../src/transform'; 2 | 3 | function removeValidate(questions: Question[]) { 4 | return questions.map(question => { 5 | const { validate, ...extra } = question as any; 6 | return extra; 7 | }); 8 | } 9 | describe('transform form', () => { 10 | it('input', () => { 11 | const schema: Schema = { 12 | type: 'object', 13 | properties: { 14 | language: { 15 | type: 'string', 16 | title: '开发语言', 17 | default: 'ts', 18 | }, 19 | }, 20 | }; 21 | const questions = transformForm(schema, {}, {}, {}); 22 | expect(removeValidate(questions)).toEqual([ 23 | { 24 | type: 'input', 25 | name: 'language', 26 | message: '开发语言', 27 | default: 'ts', 28 | origin: {}, 29 | when: true, 30 | }, 31 | ]); 32 | }); 33 | it('select', () => { 34 | const schema: Schema = { 35 | type: 'object', 36 | properties: { 37 | language: { 38 | type: 'string', 39 | title: '开发语言', 40 | default: 'ts', 41 | enum: [ 42 | { label: 'TS', value: 'ts' }, 43 | { label: 'ES6+', value: 'js' }, 44 | ], 45 | }, 46 | }, 47 | }; 48 | const questions = transformForm(schema, {}, {}, {}); 49 | 50 | expect(removeValidate(questions)).toEqual([ 51 | { 52 | type: 'list', 53 | name: 'language', 54 | message: '开发语言', 55 | default: 'ts', 56 | when: true, 57 | choices: [ 58 | { 59 | type: 'choice', 60 | name: 'TS', 61 | value: 'ts', 62 | }, 63 | { 64 | type: 'choice', 65 | name: 'ES6+', 66 | value: 'js', 67 | }, 68 | ], 69 | origin: {}, 70 | }, 71 | ]); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /packages/formily/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["../src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/formily/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/global/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | modern.config.* 41 | tsconfig.json 42 | CHANGELOG.md 43 | -------------------------------------------------------------------------------- /packages/global/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @modern-js/codesmith-global 2 | 3 | ## 2.6.8 4 | 5 | ## 2.6.7 6 | 7 | ## 2.6.6 8 | 9 | ## 2.6.5 10 | 11 | ## 2.6.4 12 | 13 | ## 2.6.3 14 | 15 | ## 2.6.2 16 | 17 | ## 2.6.1 18 | 19 | ## 2.6.0 20 | -------------------------------------------------------------------------------- /packages/global/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/global/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/global/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | export default defineConfig({ 4 | buildConfig: { 5 | target: 'es2020', 6 | dts: false, 7 | format: 'umd', 8 | autoExternal: false, 9 | alias: { 10 | '@modern-js/utils/lodash': require.resolve( 11 | '@modern-js/codesmith-utils/lodash', 12 | ), 13 | }, 14 | minify: 'terser', 15 | }, 16 | plugins: [moduleTools()], 17 | }); 18 | -------------------------------------------------------------------------------- /packages/global/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/codesmith-global", 3 | "description": "codesmith core implement", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "main": "./dist/index.js", 17 | "publishConfig": { 18 | "registry": "https://registry.npmjs.org/", 19 | "access": "public" 20 | }, 21 | "scripts": { 22 | "prepare": "pnpm build", 23 | "prepublishOnly": "pnpm build -- --platform", 24 | "new": "modern new", 25 | "build": "modern build", 26 | "test": "echo 'No tests'" 27 | }, 28 | "devDependencies": { 29 | "@modern-js/codesmith": "workspace:*", 30 | "@modern-js/codesmith-api-app": "workspace:*", 31 | "@modern-js/codesmith-api-git": "workspace:*", 32 | "@modern-js/codesmith-api-npm": "workspace:*", 33 | "@modern-js/codesmith-api-ejs": "workspace:*", 34 | "@modern-js/codesmith-api-fs": "workspace:*", 35 | "@modern-js/codesmith-api-handlebars": "workspace:*", 36 | "@modern-js/codesmith-api-json": "workspace:*", 37 | "@modern-js/codesmith-formily": "workspace:*", 38 | "@modern-js/codesmith-utils": "workspace:*", 39 | "@modern-js/plugin-i18n": "2.60.3", 40 | "@modern-js/module-tools": "2.60.3", 41 | "@types/node": "^14", 42 | "typescript": "^5" 43 | }, 44 | "sideEffects": false 45 | } 46 | -------------------------------------------------------------------------------- /packages/global/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as codesmith from '@modern-js/codesmith'; 2 | import * as codesmithApiApp from '@modern-js/codesmith-api-app'; 3 | import * as codesmithApiEjs from '@modern-js/codesmith-api-ejs'; 4 | import * as codesmithApiFs from '@modern-js/codesmith-api-fs'; 5 | import * as codesmithApiGit from '@modern-js/codesmith-api-git'; 6 | import * as codesmithApiHandlebars from '@modern-js/codesmith-api-handlebars'; 7 | import * as codesmithApiJson from '@modern-js/codesmith-api-json'; 8 | import * as codesmithApiNpm from '@modern-js/codesmith-api-npm'; 9 | import * as codesmithFormily from '@modern-js/codesmith-formily'; 10 | import * as codesmithUtils from '@modern-js/codesmith-utils'; 11 | import * as codesmithChalkUtils from '@modern-js/codesmith-utils/chalk'; 12 | import * as codesmithExecaUtils from '@modern-js/codesmith-utils/execa'; 13 | import * as codesmithFsUtils from '@modern-js/codesmith-utils/fs-extra'; 14 | import * as codesmithGlobUtils from '@modern-js/codesmith-utils/glob'; 15 | import * as codesmithLodashUtils from '@modern-js/codesmith-utils/lodash'; 16 | import * as codesmithNpmUtils from '@modern-js/codesmith-utils/npm'; 17 | import * as codesmithOraUtils from '@modern-js/codesmith-utils/ora'; 18 | import * as codesmithSemverUtils from '@modern-js/codesmith-utils/semver'; 19 | import * as pluginI18N from '@modern-js/plugin-i18n'; 20 | 21 | (global as any).codesmith = codesmith; 22 | (global as any).codesmithApiApp = codesmithApiApp; 23 | (global as any).codesmithApiJson = codesmithApiJson; 24 | (global as any).codesmithApiGit = codesmithApiGit; 25 | (global as any).codesmithUtils = codesmithUtils; 26 | (global as any).codesmithGlobUtils = codesmithGlobUtils; 27 | (global as any).codesmithLodashUtils = codesmithLodashUtils; 28 | (global as any).codesmithFsUtils = codesmithFsUtils; 29 | (global as any).codesmithChalkUtils = codesmithChalkUtils; 30 | (global as any).codesmithExecaUtils = codesmithExecaUtils; 31 | (global as any).codesmithNpmUtils = codesmithNpmUtils; 32 | (global as any).codesmithOraUtils = codesmithOraUtils; 33 | (global as any).codesmithSemverUtils = codesmithSemverUtils; 34 | (global as any).codesmithFormily = codesmithFormily; 35 | (global as any).codesmithApiNpm = codesmithApiNpm; 36 | (global as any).codesmithApiEjs = codesmithApiEjs; 37 | (global as any).codesmithApiFs = codesmithApiFs; 38 | (global as any).codesmithApiHandlebars = codesmithApiHandlebars; 39 | (global as any).pluginI18N = pluginI18N; 40 | -------------------------------------------------------------------------------- /packages/global/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/inquirer/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | 41 | modern.config.* 42 | tsconfig.json 43 | CHANGELOG.md 44 | -------------------------------------------------------------------------------- /packages/inquirer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @modern-js/inquirer-types 2 | 3 | ## 2.6.8 4 | 5 | ### Patch Changes 6 | 7 | - @modern-js/codesmith-utils@2.6.8 8 | 9 | ## 2.6.7 10 | 11 | ### Patch Changes 12 | 13 | - @modern-js/codesmith-utils@2.6.7 14 | 15 | ## 2.6.6 16 | 17 | ### Patch Changes 18 | 19 | - Updated dependencies [29e8f3d] 20 | - @modern-js/codesmith-utils@2.6.6 21 | 22 | ## 2.6.5 23 | 24 | ### Patch Changes 25 | 26 | - @modern-js/codesmith-utils@2.6.5 27 | 28 | ## 2.6.4 29 | 30 | ### Patch Changes 31 | 32 | - @modern-js/codesmith-utils@2.6.4 33 | 34 | ## 2.6.3 35 | 36 | ### Patch Changes 37 | 38 | - @modern-js/codesmith-utils@2.6.3 39 | 40 | ## 2.6.2 41 | 42 | ### Patch Changes 43 | 44 | - @modern-js/codesmith-utils@2.6.2 45 | 46 | ## 2.6.1 47 | 48 | ### Patch Changes 49 | 50 | - @modern-js/codesmith-utils@2.6.1 51 | 52 | ## 2.6.0 53 | 54 | ### Patch Changes 55 | 56 | - @modern-js/codesmith-utils@2.6.0 57 | 58 | ## 2.5.2 59 | 60 | ## 2.5.1 61 | 62 | ## 2.5.0 63 | 64 | ## 2.4.2 65 | 66 | ## 2.4.1 67 | 68 | ### Patch Changes 69 | 70 | - 798f3d9: feat: npm publish ignore src file 71 | 72 | ## 2.4.0 73 | 74 | ## 2.3.6 75 | 76 | ## 2.3.5 77 | 78 | ## 2.3.4 79 | 80 | ## 2.3.3 81 | 82 | ## 2.3.2 83 | 84 | ## 2.3.1 85 | 86 | ## 2.3.0 87 | 88 | ### Minor Changes 89 | 90 | - 562a50f: feat: upgrade modern version && update module output path 91 | 92 | ## 2.2.8 93 | 94 | ## 2.2.7 95 | 96 | ## 2.2.6 97 | 98 | ### Patch Changes 99 | 100 | - de1d271: feat: bump modern dependencies version 101 | 102 | fea: 升级 modern 依赖版本 103 | 104 | - eb5ec83: chore: publishConfig add provenance config 105 | 106 | chore: publishConfig 增加 provenance 配置 107 | 108 | ## 2.2.5 109 | 110 | ## 2.2.4 111 | 112 | ## 2.2.3 113 | 114 | ## 2.2.2 115 | 116 | ## 2.2.1 117 | 118 | ## 2.2.0 119 | 120 | ### Minor Changes 121 | 122 | - f7d3176: chore: bump modern version && fix @modern-js/utils version 123 | 124 | ## 2.1.1 125 | 126 | ## 2.1.0 127 | 128 | ### Minor Changes 129 | 130 | - 1452195: feat: upgrade modern version to v2 131 | 132 | ## 2.0.5 133 | 134 | ## 2.0.4 135 | 136 | ## 2.0.3 137 | 138 | ## 2.0.2 139 | 140 | ## 2.0.1 141 | 142 | ## 2.0.0 143 | 144 | ## 1.6.3 145 | 146 | ## 1.6.2 147 | 148 | ## 1.6.1 149 | 150 | ## 1.6.0 151 | 152 | ## 1.5.1 153 | 154 | ## 1.5.0 155 | 156 | ## 1.3.0 157 | 158 | ### Minor Changes 159 | 160 | - e71e509: feat: adjust `package.json` field, delete `module` and `exports` field 161 | 162 | feat: 调整 `package.json` 中字段,删除 `module` 和 `exports` 字段 163 | 164 | ## 1.2.2 165 | 166 | ### Patch Changes 167 | 168 | - 7430860: fix: rxjs version 169 | 170 | ## 1.0.9 171 | 172 | ### Patch Changes 173 | 174 | - c40ea75: feat: bump @modern-js/utils version 175 | 176 | ## 1.0.8 177 | 178 | ### Patch Changes 179 | 180 | - 4791c02: feat: using prebundled deps from @modern-js/utils 181 | 182 | ## 1.0.7 183 | 184 | ### Patch Changes 185 | 186 | - d1f4956: feat: support noNeedInstall params 187 | 188 | ## 1.0.6 189 | 190 | ### Patch Changes 191 | 192 | - 0af3795: feat: support change git init branch 193 | 194 | ## 1.0.5 195 | 196 | ### Patch Changes 197 | 198 | - fix: build product 199 | 200 | ## 1.0.4 201 | 202 | ### Patch Changes 203 | 204 | - feat: export codesmith utils 205 | 206 | ## 1.0.3 207 | 208 | ### Patch Changes 209 | 210 | - 3ef5a43: feat: add release workflow 211 | 212 | ## 1.0.2 213 | 214 | ### Patch Changes 215 | 216 | - a6a1c5c: feat: remove run install '--ignore-scripts' params 217 | 218 | ## 1.0.1 219 | 220 | ### Patch Changes 221 | 222 | - fix: publish not container dist 223 | 224 | ## 1.0.0 225 | 226 | ### Patch Changes 227 | 228 | - feat: initial publish 229 | -------------------------------------------------------------------------------- /packages/inquirer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/inquirer/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/inquirer/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | import { testingPlugin } from '@modern-js/plugin-testing'; 4 | 5 | export default defineConfig({ 6 | buildPreset: 'modern-js-universal', 7 | plugins: [moduleTools(), testingPlugin()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/inquirer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js/inquirer-types", 3 | "description": "codesmith inquirer plugin to custom question", 4 | "homepage": "https://modernjs.dev", 5 | "bugs": "https://github.com/web-infra-dev/codesmith/issues", 6 | "repository": "web-infra-dev/codesmith", 7 | "license": "MIT", 8 | "keywords": [ 9 | "react", 10 | "framework", 11 | "modern", 12 | "modern.js" 13 | ], 14 | "version": "2.6.8", 15 | "jsnext:source": "./src/index.ts", 16 | "types": "./dist/types/index.d.ts", 17 | "main": "./dist/cjs/index.js", 18 | "module": "./dist/esm-node/index.js", 19 | "publishConfig": { 20 | "registry": "https://registry.npmjs.org/", 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "prepare": "pnpm build", 25 | "prepublishOnly": "pnpm build -- --platform", 26 | "new": "modern new", 27 | "build": "modern build", 28 | "test": "modern test --passWithNoTests" 29 | }, 30 | "dependencies": { 31 | "@swc/helpers": "0.5.1", 32 | "cli-cursor": "^3.1.0", 33 | "inquirer": "8.1.3", 34 | "run-async": "^2.4.1", 35 | "rxjs": "^7.8.0", 36 | "@modern-js/codesmith-utils": "workspace:*" 37 | }, 38 | "devDependencies": { 39 | "@modern-js/module-tools": "2.60.3", 40 | "@modern-js/plugin-testing": "2.60.3", 41 | "@types/inquirer": "^7.3.3", 42 | "@types/jest": "^26.0.24", 43 | "@types/lodash": "^4.14.192", 44 | "@types/node": "^14.18.42", 45 | "typescript": "^4.9.5" 46 | }, 47 | "sideEffects": false 48 | } 49 | -------------------------------------------------------------------------------- /packages/inquirer/src/index.ts: -------------------------------------------------------------------------------- 1 | export { List } from './list'; 2 | -------------------------------------------------------------------------------- /packages/inquirer/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'inquirer/lib/utils/incrementListIndex' { 2 | export default function incrementListIndex( 3 | current: number, 4 | dir: string, 5 | opt: { loop: boolean }, 6 | ): number; 7 | } 8 | 9 | declare module 'run-async'; 10 | -------------------------------------------------------------------------------- /packages/inquirer/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { chalk } from '@modern-js/codesmith-utils/chalk'; 2 | import type { Answers } from 'inquirer'; 3 | import type OriginChoices from 'inquirer/lib/objects/choices'; 4 | import { pointer as pointerCharacter } from './pointer'; 5 | 6 | export type ChoiceItem = Answers & { 7 | disabled: boolean | ((answers: Answers) => boolean); 8 | }; 9 | 10 | export type Choices = OriginChoices & { 11 | type?: 'separator' | 'choices'; 12 | }; 13 | 14 | /** 15 | * Function for rendering list choices 16 | * @param {Number} pointer Position of the pointer 17 | * @return {String} Rendered content 18 | */ 19 | export function listRender( 20 | choices: Choices, 21 | pointer: number, 22 | answers: Answers, 23 | ): string { 24 | let output = ''; 25 | let separatorOffset = 0; 26 | 27 | choices.forEach((choice, i) => { 28 | if (choice.type === 'separator') { 29 | separatorOffset++; 30 | // separator class toString result 31 | output += ` ${choice as any as string}\n`; 32 | return; 33 | } 34 | 35 | if (choice.disabled) { 36 | // TODO confirm 37 | if (typeof choice.disabled === 'function') { 38 | choice.disabled = (choice.disabled as (answers: Answers) => boolean)( 39 | answers, 40 | ); 41 | } 42 | separatorOffset++; 43 | let choiceItem = ''; 44 | choiceItem += ` - ${choice.name}`; 45 | choiceItem += ` (${ 46 | typeof choice.disabled === 'string' ? choice.disabled : 'Disabled' 47 | })`; 48 | output += chalk.dim(choiceItem); 49 | output += '\n'; 50 | return; 51 | } 52 | 53 | const isSelected = i - separatorOffset === pointer; 54 | 55 | let line = (isSelected ? `${pointerCharacter} ` : ' ') + choice.name; 56 | 57 | if (isSelected) { 58 | line = chalk.cyan(line); 59 | } 60 | output += `${line} \n`; 61 | }); 62 | 63 | return output.replace(/\n$/, ''); 64 | } 65 | -------------------------------------------------------------------------------- /packages/inquirer/src/utils/pointer.ts: -------------------------------------------------------------------------------- 1 | function isUnicodeSupported() { 2 | if (process.platform !== 'win32') { 3 | return process.env.TERM !== 'linux'; // Linux console (kernel) 4 | } 5 | 6 | return ( 7 | Boolean(process.env.CI) || 8 | Boolean(process.env.WT_SESSION) || // Windows Terminal 9 | process.env.ConEmuTask === '{cmd::Cmder}' || // ConEmu and cmder 10 | process.env.TERM_PROGRAM === 'vscode' || 11 | process.env.TERM === 'xterm-256color' || 12 | process.env.TERM === 'alacritty' 13 | ); 14 | } 15 | 16 | const shouldUseMain = isUnicodeSupported(); 17 | const pointer = shouldUseMain ? '❯' : '>'; 18 | 19 | export { pointer }; 20 | -------------------------------------------------------------------------------- /packages/inquirer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/utils/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .pnp 4 | .pnp.js 5 | .env.*.local 6 | .history 7 | .rts* 8 | *.log* 9 | *.pid 10 | *.pid.* 11 | *.report 12 | *.lcov 13 | lib-cov 14 | 15 | node_modules/ 16 | .npm 17 | .lock-wscript 18 | .yarn-integrity 19 | .node_repl_history 20 | .nyc_output 21 | *.tsbuildinfo 22 | .eslintcache 23 | .sonarlint 24 | 25 | coverage/ 26 | release/ 27 | output/ 28 | output_resource/ 29 | 30 | .vscode/**/* 31 | !.vscode/settings.json 32 | !.vscode/extensions.json 33 | .idea/ 34 | 35 | **/*/api/typings/auto-generated 36 | **/*/adapters/**/index.ts 37 | **/*/adapters/**/index.js 38 | 39 | src/ 40 | modern.config.* 41 | tsconfig.json 42 | CHANGELOG.md 43 | -------------------------------------------------------------------------------- /packages/utils/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @modern-js/codesmith-utils 2 | 3 | ## 2.6.8 4 | 5 | ## 2.6.7 6 | 7 | ## 2.6.6 8 | 9 | ### Patch Changes 10 | 11 | - 29e8f3d: feat: support fnm 12 | 13 | feat: 支持 fnm 14 | 15 | ## 2.6.5 16 | 17 | ## 2.6.4 18 | 19 | ## 2.6.3 20 | 21 | ## 2.6.2 22 | 23 | ## 2.6.1 24 | 25 | ## 2.6.0 26 | -------------------------------------------------------------------------------- /packages/utils/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modern.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 |

2 | Modern.js Logo 3 |

4 | 5 |

Modern.js

6 | 7 |

8 | A Progressive React Framework for modern web development. 9 |

10 | 11 | ## Getting Started 12 | 13 | Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js. 14 | 15 | ## Documentation 16 | 17 | - [English Documentation](https://modernjs.dev/en/) 18 | - [中文文档](https://modernjs.dev) 19 | 20 | ## Contributing 21 | 22 | Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/blob/main/CONTRIBUTING.md). 23 | 24 | ## License 25 | 26 | Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE). 27 | -------------------------------------------------------------------------------- /packages/utils/modern.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, moduleTools } from '@modern-js/module-tools'; 2 | 3 | export default defineConfig({ 4 | buildPreset: 'modern-js-universal', 5 | plugins: [moduleTools()], 6 | }); 7 | -------------------------------------------------------------------------------- /packages/utils/src/chalk.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export { chalk }; 4 | -------------------------------------------------------------------------------- /packages/utils/src/execa.ts: -------------------------------------------------------------------------------- 1 | import execa, { type ExecaReturnValue } from 'execa'; 2 | 3 | export { execa, type ExecaReturnValue }; 4 | -------------------------------------------------------------------------------- /packages/utils/src/fs-extra.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | 3 | export { fs }; 4 | -------------------------------------------------------------------------------- /packages/utils/src/glob.ts: -------------------------------------------------------------------------------- 1 | import glob from 'glob'; 2 | 3 | export { glob }; 4 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chalk'; 2 | export * from './execa'; 3 | export * from './fs-extra'; 4 | export * from './glob'; 5 | export * from './lodash'; 6 | export * from './ora'; 7 | export * from './semver'; 8 | export * from './npm'; 9 | -------------------------------------------------------------------------------- /packages/utils/src/lodash.ts: -------------------------------------------------------------------------------- 1 | import flattenDeep from 'lodash.flattendeep'; 2 | import get from 'lodash.get'; 3 | import isFunction from 'lodash.isfunction'; 4 | import isObject from 'lodash.isobject'; 5 | import isString from 'lodash.isstring'; 6 | import merge from 'lodash.merge'; 7 | 8 | export { merge, isFunction, isObject, get, isString, flattenDeep }; 9 | -------------------------------------------------------------------------------- /packages/utils/src/npm.ts: -------------------------------------------------------------------------------- 1 | import execa from 'execa'; 2 | 3 | export async function canUseNvm() { 4 | try { 5 | await execa('source ~/.nvm/nvm.sh', { 6 | env: process.env, 7 | shell: true, 8 | }); 9 | return true; 10 | } catch (e) { 11 | return false; 12 | } 13 | } 14 | 15 | export async function canUseFnm() { 16 | try { 17 | await execa('fnm --version', { 18 | env: process.env, 19 | shell: true, 20 | }); 21 | return true; 22 | } catch (e) { 23 | return false; 24 | } 25 | } 26 | 27 | export async function canUseNpm() { 28 | try { 29 | await execa('npm', ['--version'], { 30 | env: process.env, 31 | }); 32 | return true; 33 | } catch (e) { 34 | return false; 35 | } 36 | } 37 | 38 | export async function canUseYarn() { 39 | try { 40 | await execa('yarn', ['--version'], { 41 | env: process.env, 42 | }); 43 | return true; 44 | } catch (e) { 45 | return false; 46 | } 47 | } 48 | 49 | export async function canUsePnpm() { 50 | try { 51 | await execa('pnpm', ['--version'], { 52 | env: process.env, 53 | }); 54 | return true; 55 | } catch (e) { 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/utils/src/ora.ts: -------------------------------------------------------------------------------- 1 | import ora from 'ora'; 2 | 3 | export { ora }; 4 | -------------------------------------------------------------------------------- /packages/utils/src/semver.ts: -------------------------------------------------------------------------------- 1 | import semver from 'semver'; 2 | 3 | export { semver }; 4 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve", 6 | "baseUrl": "./", 7 | "isolatedModules": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/**' 3 | - 'examples/**' 4 | - 'services/**' 5 | - 'features/**' 6 | - 'packages/**' 7 | - 'packages/api/**' 8 | - 'packages/easy-form/**' 9 | - 'scripts' 10 | -------------------------------------------------------------------------------- /scripts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: '@modern-js', 3 | parserOptions: { 4 | project: require.resolve('./tsconfig.json'), 5 | }, 6 | rules: { 7 | 'no-console': 'off', 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modern-js-codesmith/scripts", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "get-release-version": "tsx ./src/get-release-version.ts" 7 | }, 8 | "dependencies": { 9 | "@changesets/assemble-release-plan": "^5.2.1", 10 | "@changesets/config": "^2.1.1", 11 | "@changesets/read": "^0.6.0", 12 | "@manypkg/get-packages": "^1.1.3" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "^18.0.1", 16 | "tsx": "~3.12.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/src/get-release-version.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import assembleReleasePlan from '@changesets/assemble-release-plan'; 3 | import { read } from '@changesets/config'; 4 | import readChangesets from '@changesets/read'; 5 | import { getPackages } from '@manypkg/get-packages'; 6 | 7 | async function run() { 8 | const cwd = process.cwd(); 9 | const repoDir = path.join(cwd, '..'); 10 | const changesets = await readChangesets(repoDir, process.env.BASE_BRANCH); 11 | const packages = await getPackages(repoDir); 12 | const config = await read(repoDir, packages); 13 | const releasePlan = assembleReleasePlan( 14 | changesets, 15 | packages, 16 | config, 17 | undefined, 18 | ); 19 | if (releasePlan.releases.length === 0) { 20 | return; 21 | } 22 | const releaseVersion = `v${releasePlan.releases[0].newVersion}`; 23 | console.log(releaseVersion); 24 | } 25 | 26 | run().catch(e => { 27 | console.error(e); 28 | // eslint-disable-next-line no-process-exit 29 | process.exit(1); 30 | }); 31 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/base", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@modern-js/tsconfig/react", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "jsx": "preserve" 6 | } 7 | } 8 | --------------------------------------------------------------------------------