├── .nvmrc ├── output ├── .gitkeep ├── sample-02.ts ├── sample-03.ts └── sample-01.ts ├── .gitattributes ├── .npmrc ├── src ├── run-sample1.ts ├── run-sample2.ts ├── run-sample3.ts ├── __tests__ │ ├── __snapshots__ │ │ ├── sample-02.test.ts.snap │ │ ├── sample-03.test.ts.snap │ │ └── sample-01.test.ts.snap │ ├── sample-01.test.ts │ ├── sample-02.test.ts │ └── sample-03.test.ts ├── sample-01.ts ├── sample-03.ts ├── sample-02.ts └── utils.ts ├── tsconfig.test.json ├── .gitignore ├── tsup.config.ts ├── .github ├── actions │ └── initialize │ │ └── action.yml └── workflows │ └── build.yml ├── README.md ├── biome.json ├── tsconfig.json ├── LICENSE └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * eol=lf 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | always-auth=true 2 | engine-strict=true 3 | -------------------------------------------------------------------------------- /src/run-sample1.ts: -------------------------------------------------------------------------------- 1 | import { run } from "./sample-01"; 2 | 3 | run(); 4 | -------------------------------------------------------------------------------- /src/run-sample2.ts: -------------------------------------------------------------------------------- 1 | import { run } from "./sample-02"; 2 | 3 | run(); 4 | -------------------------------------------------------------------------------- /src/run-sample3.ts: -------------------------------------------------------------------------------- 1 | import { run } from "./sample-03"; 2 | 3 | run(); 4 | -------------------------------------------------------------------------------- /output/sample-02.ts: -------------------------------------------------------------------------------- 1 | const hello = () => { 2 | return "world"; 3 | }; 4 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["output"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | lib 3 | coverage 4 | node_modules 5 | package-lock.json 6 | .env* 7 | *.log 8 | private_npm_cache 9 | dist 10 | -------------------------------------------------------------------------------- /output/sample-03.ts: -------------------------------------------------------------------------------- 1 | const mainTask = () => { 2 | const subTask = () => {} 3 | return subTask(); 4 | } 5 | mainTask(); 6 | 7 | 8 | -------------------------------------------------------------------------------- /output/sample-01.ts: -------------------------------------------------------------------------------- 1 | const mainTask1 = () => { 2 | const subTask = () => { }; 3 | return subTask(); 4 | }; 5 | mainTask1(); 6 | // SampleCode 7 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/sample-02.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Sample2: Code Generate Test 1`] = ` 4 | "const hello = () => { 5 | return "world"; 6 | }; 7 | " 8 | `; 9 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | minify: false, 6 | target: "es2022", 7 | format: ["cjs", "esm"], 8 | clean: true, 9 | dts: true, 10 | sourcemap: true, 11 | }); 12 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/sample-03.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Sample3: Code Generate Test 1`] = ` 4 | "const mainTask = () => { 5 | const subTask = () => {} 6 | return subTask(); 7 | } 8 | mainTask(); 9 | 10 | 11 | " 12 | `; 13 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/sample-01.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Sample1: Code Generate Test 1`] = ` 4 | "const mainTask1 = () => { 5 | const subTask = () => { }; 6 | return subTask(); 7 | }; 8 | mainTask1(); 9 | // SampleCode 10 | " 11 | `; 12 | -------------------------------------------------------------------------------- /src/__tests__/sample-01.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import { test, expect } from "vitest"; 3 | import * as Module from "../sample-01"; 4 | 5 | test("Sample1: Code Generate Test", () => { 6 | Module.run(); 7 | 8 | const generateCode = fs.readFileSync("output/sample-01.ts", { 9 | encoding: "utf-8", 10 | }); 11 | expect(generateCode).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/__tests__/sample-02.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import { test, expect } from "vitest"; 3 | import * as Module from "../sample-02"; 4 | 5 | test("Sample2: Code Generate Test", () => { 6 | Module.run(); 7 | 8 | const generateCode = fs.readFileSync("output/sample-02.ts", { 9 | encoding: "utf-8", 10 | }); 11 | expect(generateCode).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/__tests__/sample-03.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import { test, expect } from "vitest"; 3 | import * as Module from "../sample-03"; 4 | 5 | test("Sample3: Code Generate Test", () => { 6 | Module.run(); 7 | 8 | const generateCode = fs.readFileSync("output/sample-03.ts", { 9 | encoding: "utf-8", 10 | }); 11 | expect(generateCode).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /.github/actions/initialize/action.yml: -------------------------------------------------------------------------------- 1 | name: "initialize" 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Setup Git Config 7 | run: | 8 | git config --global core.autocrlf false 9 | git config --global core.eol lf 10 | git config --global user.email "actions@gihub.com" 11 | git config --global user.name "gh-actions" 12 | shell: bash 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code Generator Sample 2 | 3 | ```bash 4 | # Please Clone this repository, and you can setup with below commands 5 | npm i -g pnpm 6 | pnpm install 7 | ``` 8 | 9 | ```bash 10 | # Run Code 11 | pnpm run ts ./src/run-sample1.ts 12 | pnpm run ts ./src/run-sample2.ts 13 | pnpm run ts ./src/run-sample3.ts 14 | 15 | # Watch Start 16 | pnpm run watch:type-check 17 | pnpm run watch:vitest 18 | ``` 19 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "formatter": { 7 | "enabled": true, 8 | "indentStyle": "space", 9 | "lineWidth": 180 10 | }, 11 | "linter": { 12 | "enabled": true, 13 | "rules": { 14 | "recommended": true 15 | } 16 | }, 17 | "files": { 18 | "ignoreUnknown": true, 19 | "ignore": ["dist", "output"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/sample-01.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | 3 | import * as Printer from "./utils"; 4 | 5 | export const run = () => { 6 | const sourceText = `const mainTask1 = () => { 7 | const subTask = () => {} 8 | return subTask(); 9 | } 10 | mainTask1(); 11 | // SampleCode 12 | `; 13 | 14 | const code = Printer.generateCodeByPlainText(sourceText); 15 | 16 | fs.writeFileSync("output/sample-01.ts", code, "utf8"); 17 | 18 | console.log("Generated output/sample-01.ts"); 19 | }; 20 | -------------------------------------------------------------------------------- /src/sample-03.ts: -------------------------------------------------------------------------------- 1 | import { Project, ts } from "ts-morph"; 2 | 3 | export const run = () => { 4 | const text = `const mainTask = () => { 5 | const subTask = () => {} 6 | return subTask(); 7 | } 8 | mainTask(); 9 | 10 | `; 11 | 12 | const project = new Project(); 13 | const sourceFile = project.createSourceFile("output/sample-03.ts", "", { 14 | overwrite: true, 15 | }); 16 | 17 | sourceFile.addStatements([text]); 18 | 19 | project.saveSync(); 20 | 21 | console.log("Generated output/sample-03.ts"); 22 | }; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react", 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noFallthroughCasesInSwitch": true, 11 | "noUnusedParameters": false, 12 | "skipLibCheck": true, 13 | "sourceMap": false, 14 | "target": "es2022", 15 | "lib": ["dom", "es2023"], 16 | "rootDir": ".", 17 | "outDir": "lib", 18 | "strict": true 19 | }, 20 | "ts-node": { 21 | "esm": true, 22 | "swc": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | node-version: [20.x] 13 | os: [ubuntu-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: ./.github/actions/initialize 18 | - uses: pnpm/action-setup@v2.2.2 19 | with: 20 | version: 8.15.1 21 | - uses: actions/setup-node@v2 22 | with: 23 | node-version: "20.x" 24 | cache: "pnpm" 25 | - run: pnpm i --frozen-lockfile 26 | - name: Use Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v1 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - name: Test & Build 31 | run: | 32 | pnpm build 33 | pnpm test 34 | env: 35 | CI: true 36 | -------------------------------------------------------------------------------- /src/sample-02.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import * as fs from "node:fs"; 3 | 4 | import * as Printer from "./utils"; 5 | 6 | const factory = ts.factory; 7 | 8 | export const run = () => { 9 | const code = Printer.generateCodeByStatements([ 10 | factory.createVariableStatement( 11 | undefined, 12 | factory.createVariableDeclarationList( 13 | [ 14 | factory.createVariableDeclaration( 15 | factory.createIdentifier("hello"), 16 | undefined, 17 | undefined, 18 | factory.createArrowFunction( 19 | undefined, 20 | undefined, 21 | [], 22 | undefined, 23 | factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 24 | factory.createBlock([factory.createReturnStatement(factory.createStringLiteral("world"))], true), 25 | ), 26 | ), 27 | ], 28 | ts.NodeFlags.Const, 29 | ), 30 | ), 31 | ]); 32 | 33 | fs.writeFileSync("output/sample-02.ts", code, "utf8"); 34 | 35 | console.log("Generated output/sample-02.ts"); 36 | }; 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Himenon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | 3 | export type TransformerFactory = ts.TransformerFactory; 4 | 5 | /** 6 | * Traverse処理なし 7 | */ 8 | export const generateCodeByPlainText = (sourceText: string): string => { 9 | // ========== 10 | // 抽象木の作成 11 | // ========== 12 | const source = ts.createSourceFile("sample.ts", sourceText, ts.ScriptTarget.ESNext); 13 | // ============== 14 | // Transformation 15 | // ============== 16 | const result = ts.transform(source, []); 17 | result.dispose(); 18 | 19 | // ============== 20 | // Code Generate 21 | // ============== 22 | const printer = ts.createPrinter(); 23 | const transformedSourceFile = result.transformed[0]; 24 | return printer.printFile(transformedSourceFile); 25 | }; 26 | 27 | export const generateCodeByStatements = (statements: ts.Statement[]) => { 28 | const sourceFileNode = ts.createSourceFile( 29 | "", 30 | "", // empty text 31 | ts.ScriptTarget.ESNext, 32 | ); 33 | 34 | const transformedSourceFile = ts.factory.updateSourceFile( 35 | sourceFileNode, 36 | statements, 37 | sourceFileNode.isDeclarationFile, 38 | sourceFileNode.referencedFiles, 39 | sourceFileNode.typeReferenceDirectives, 40 | sourceFileNode.hasNoDefaultLib, 41 | sourceFileNode.libReferenceDirectives, 42 | ); 43 | 44 | const printer = ts.createPrinter(); 45 | return printer.printFile(transformedSourceFile); 46 | }; 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@himenon/template-esm-js", 3 | "version": "1.1.6", 4 | "description": "ESModule Library Template", 5 | "keywords": ["template", "typescript"], 6 | "homepage": "https://github.com/Himenon/template-esm-js#readme", 7 | "bugs": { 8 | "url": "https://github.com/Himenon/template-esm-js/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:Himenon/template-esm-js.git" 13 | }, 14 | "license": "MIT", 15 | "author": { 16 | "name": "Himenon", 17 | "email": "k.himeno314@gmail.com", 18 | "url": "https://github.com/Himenon" 19 | }, 20 | "sideEffects": false, 21 | "type": "module", 22 | "exports": { 23 | ".": { 24 | "import": "./dist/index.js", 25 | "require": "./dist/index.cjs", 26 | "node": "./dist/index.cjs", 27 | "default": "./dist/index.js" 28 | }, 29 | "./package.json": "./package.json" 30 | }, 31 | "main": "./dist/index.cjs", 32 | "module": "./dist/index.js", 33 | "types": "./dist/index.d.ts", 34 | "directories": { 35 | "dist": "dist" 36 | }, 37 | "files": ["dist", "package.json", "README.md"], 38 | "scripts": { 39 | "start": "pnpm ts ./src/index.ts", 40 | "build": "tsup src/*.ts", 41 | "clean": "rimraf dist", 42 | "lerna:version:up": "lerna version --yes", 43 | "release:github:registry": "pnpm publish --registry https://npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}", 44 | "release:npm:registry": "pnpm publish", 45 | "test": "run-p test:vitest lint", 46 | "lint": "biome lint .", 47 | "format": "biome format --write .", 48 | "validate": "biome check --apply .", 49 | "test:vitest": "vitest", 50 | "watch:vitest": "vitest --update", 51 | "run:sample:1": "pnpm run ts ./src/run-sample1.ts", 52 | "run:sample:2": "pnpm run ts ./src/run-sample2.ts", 53 | "run:sample:3": "pnpm run ts ./src/run-sample3.ts", 54 | "watch:type-check": "tsc --noEmit -w -p ./tsconfig.test.json", 55 | "ts": "node --no-warnings=ExperimentalWarning --experimental-specifier-resolution=node --loader ts-node/esm" 56 | }, 57 | "simple-git-hooks": { 58 | "pre-commit": "pnpm lint-staged" 59 | }, 60 | "lint-staged": { 61 | "*.{js,jsx,json,yml,yaml,html,md,ts,tsx}": ["biome format --no-errors-on-unmatched --write"] 62 | }, 63 | "devDependencies": { 64 | "@biomejs/biome": "^1.6.3", 65 | "@swc/core": "^1.4.11", 66 | "@types/node": "20.12.2", 67 | "@typescript-eslint/eslint-plugin": "7.4.0", 68 | "@typescript-eslint/parser": "7.4.0", 69 | "conventional-changelog-angular-all": "1.7.0", 70 | "cross-env": "^7.0.3", 71 | "lerna": "8.1.2", 72 | "lint-staged": "15.2.2", 73 | "npm-run-all": "4.1.5", 74 | "rimraf": "^5.0.5", 75 | "simple-git-hooks": "^2.11.1", 76 | "ts-node": "^10.9.2", 77 | "tsup": "^8.0.2", 78 | "typescript": "5.4.3", 79 | "vitest": "^1.4.0" 80 | }, 81 | "engines": { 82 | "node": ">=20", 83 | "pnpm": ">=8" 84 | }, 85 | "publishConfig": { 86 | "access": "public" 87 | }, 88 | "dependencies": { 89 | "code-block-writer": "13.0.1", 90 | "ts-morph": "^22.0.0" 91 | } 92 | } 93 | --------------------------------------------------------------------------------