├── .npmignore ├── .prettierignore ├── src ├── library │ ├── @types │ │ └── global.ts │ ├── tsconfig.json │ ├── .eslintrc.json │ └── index.ts └── test │ ├── .eslintrc.json │ ├── tsconfig.json │ └── basic.test.ts ├── jest.config.js ├── .gitignore ├── tsconfig.json ├── .prettierrc ├── .github └── workflows │ └── ci.yaml ├── .eslintrc.json ├── tsconfig.base.json ├── .magicspace ├── boilerplate.json └── boilerplate.schema.json ├── .vscode └── settings.json ├── LICENSE ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !/src/library/**/*.{ts,tsx} 3 | !/bld/library/** 4 | *.tsbuildinfo 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Magicspace 2 | boilerplate.schema.json 3 | # TypeScript Build Artifacts 4 | bld/ 5 | -------------------------------------------------------------------------------- /src/library/@types/global.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | function setTimeout(callback: () => void, timeout: number): number; 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | export default { 3 | transform: {}, 4 | testMatch: ['/bld/test/*.test.js'], 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | *.tgz 4 | node_modules/ 5 | # Package Manager 6 | yarn-error.log 7 | # TypeScript Build Artifacts 8 | bld/ 9 | *.tsbuildinfo 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { 4 | "path": "src/library" 5 | }, 6 | { 7 | "path": "src/test" 8 | } 9 | ], 10 | "files": [] 11 | } 12 | -------------------------------------------------------------------------------- /src/library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "rootDir": ".", 6 | "outDir": "../../bld/library" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/library/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "overrides": [ 4 | { 5 | "files": ["**/*.{ts,tsx}"], 6 | "extends": ["plugin:@mufan/typescript"], 7 | "parserOptions": { 8 | "project": "tsconfig.json" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "overrides": [ 4 | { 5 | "files": ["**/*.{ts,tsx}"], 6 | "extends": ["plugin:@mufan/typescript", "plugin:@mufan/dev"], 7 | "parserOptions": { 8 | "project": "tsconfig.json" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "types": ["node", "jest"], 6 | "rootDir": ".", 7 | "outDir": "../../bld/test" 8 | }, 9 | "references": [ 10 | { 11 | "path": "../library" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "as-needed", 8 | "jsxSingleQuote": false, 9 | "trailingComma": "all", 10 | "bracketSpacing": false, 11 | "bracketSameLine": false, 12 | "arrowParens": "avoid" 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | container: node:20 13 | steps: 14 | - uses: actions/checkout@v3 15 | - run: yarn install 16 | - run: yarn test 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["node_modules/", "/src/", "/bld/"], 4 | "extends": ["plugin:@mufan/javascript"], 5 | "overrides": [ 6 | { 7 | "files": "**/*.{js,mjs}", 8 | "parserOptions": { 9 | "sourceType": "module" 10 | } 11 | }, 12 | { 13 | "files": "**/*.cjs", 14 | "parserOptions": { 15 | "sourceType": "script" 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "lib": ["ESNext"], 7 | "types": [], 8 | "importHelpers": true, 9 | "esModuleInterop": true, 10 | "stripInternal": true, 11 | "sourceMap": true, 12 | "strict": true, 13 | "noImplicitReturns": true, 14 | "noImplicitOverride": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "useDefineForClassFields": true, 17 | "skipLibCheck": true, 18 | "newLine": "LF" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.magicspace/boilerplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "boilerplate.schema.json", 3 | "boilerplate": "@mufan/code-boilerplates/typescript", 4 | "options": { 5 | "name": "memorize-decorator", 6 | "description": "A simple decorator that memorizes results of methods and getters.", 7 | "license": "MIT", 8 | "author": "vilicvane", 9 | "repository": "https://github.com/vilicvane/memorize-decorator.git", 10 | "defaultBranch": "master", 11 | "packageManager": "yarn", 12 | "badges": { 13 | "npm": true, 14 | "repo": true, 15 | "license": true, 16 | "discord": "https://discord.gg/vanVrDwSkS" 17 | }, 18 | "type": "module", 19 | "projects": [ 20 | { 21 | "name": "library" 22 | }, 23 | { 24 | "name": "test", 25 | "references": ["library"] 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.insertSpaces": true, 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "editor.defaultFormatter": "esbenp.prettier-vscode", 9 | "editor.rulers": [80], 10 | "files.associations": { 11 | "boilerplate.json": "jsonc" 12 | }, 13 | "files.eol": "\n", 14 | "files.insertFinalNewline": true, 15 | "files.trimTrailingWhitespace": true, 16 | "typescript.preferences.importModuleSpecifier": "project-relative", 17 | "typescript.preferences.importModuleSpecifierEnding": "js", 18 | "typescript.tsdk": "node_modules/typescript/lib", 19 | "eslint.workingDirectories": [".", "src/library", "src/test"], 20 | "eslint.validate": [ 21 | "javascript", 22 | "javascriptreact", 23 | "typescript", 24 | "typescriptreact" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 vilicvane 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memorize-decorator", 3 | "version": "0.3.1", 4 | "description": "A simple decorator that memorizes results of methods and getters.", 5 | "repository": "https://github.com/vilicvane/memorize-decorator.git", 6 | "license": "MIT", 7 | "author": "vilicvane", 8 | "type": "module", 9 | "exports": { 10 | "types": "./bld/library/index.d.ts", 11 | "default": "./bld/library/index.js" 12 | }, 13 | "scripts": { 14 | "3": "yarn && yarn-deduplicate && yarn", 15 | "build": "rimraf ./bld && tsc --build", 16 | "lint": "eslint --no-error-on-unmatched-pattern --report-unused-disable-directives . && run-in-every eslint-project --parallel --echo -- eslint --no-error-on-unmatched-pattern --report-unused-disable-directives .", 17 | "lint-prettier": "prettier --check .", 18 | "bare-test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", 19 | "test": "yarn lint-prettier && yarn build && yarn lint && yarn bare-test" 20 | }, 21 | "dependencies": { 22 | "multikey-map": "^0.2.1", 23 | "tslib": "^2.6.2" 24 | }, 25 | "devDependencies": { 26 | "@jest/globals": "^29.7.0", 27 | "@mufan/eslint-plugin": "^0.2.16", 28 | "@types/jest": "^29.5.8", 29 | "@types/node": "^20.9.0", 30 | "cross-env": "^7.0.3", 31 | "eslint": "^8.53.0", 32 | "jest": "^29.7.0", 33 | "prettier": "^3.1.0", 34 | "rimraf": "^5.0.5", 35 | "run-in-every": "^0.2.0", 36 | "typescript": "^5.2.2", 37 | "yarn-deduplicate": "^6.0.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM version](https://img.shields.io/npm/v/memorize-decorator?color=%23cb3837&style=flat-square)](https://www.npmjs.com/package/memorize-decorator) 2 | [![Repository package.json version](https://img.shields.io/github/package-json/v/vilicvane/memorize-decorator?color=%230969da&label=repo&style=flat-square)](./package.json) 3 | [![MIT License](https://img.shields.io/badge/license-MIT-999999?style=flat-square)](./LICENSE) 4 | [![Discord](https://img.shields.io/badge/chat-discord-5662f6?style=flat-square)](https://discord.gg/vanVrDwSkS) 5 | 6 | # Memorize Decorator 7 | 8 | A simple decorator that memorizes results of methods and getters. It can also wrap normal functions via the old-fashioned way. 9 | 10 | **Note:** It takes `this` and arguments (for methods and functions) as keys for the memorized results. 11 | 12 | ## Install 13 | 14 | ```sh 15 | yarn add memorize-decorator 16 | # or 17 | npm install memorize-decorator --save 18 | ``` 19 | 20 | > For **CommonJS** and **experimental decorator**, use version "0.2". 21 | 22 | ## API References 23 | 24 | ```ts 25 | export declare function memorize(fn: T): T; 26 | export declare function memorize(): MethodDecorator; 27 | 28 | export default memorize; 29 | ``` 30 | 31 | ## Usage 32 | 33 | ```ts 34 | import deprecated from 'memorize-decorator'; 35 | 36 | class Foo { 37 | @memorize({ 38 | // Delete cache after 100 milliseconds. 39 | ttl: 100, 40 | }) 41 | method() { 42 | return 'abc'; 43 | } 44 | 45 | @memorize({ 46 | // Keep cache until returned Promise gets fulfilled. 47 | ttl: 'async', 48 | }) 49 | async asyncMethod() { 50 | return 'abc'; 51 | } 52 | 53 | @memorize({ 54 | // Use `asap` package to schedule cache deletion. 55 | ttl: false, 56 | }) 57 | get property() { 58 | return 123; 59 | } 60 | 61 | @memorize() 62 | static method() { 63 | return 'abc'; 64 | } 65 | 66 | @memorize() 67 | static get property() { 68 | return 123; 69 | } 70 | } 71 | ``` 72 | 73 | For functions: 74 | 75 | ```ts 76 | import memorize from 'memorize-decorator'; 77 | 78 | let foo = memorize(function foo() { 79 | // ... 80 | }); 81 | ``` 82 | 83 | ## License 84 | 85 | MIT License. 86 | -------------------------------------------------------------------------------- /src/library/index.ts: -------------------------------------------------------------------------------- 1 | import {MultikeyMap} from 'multikey-map'; 2 | 3 | const RESOLVED = Promise.resolve(); 4 | 5 | export type MemorizeOptions = { 6 | ttl?: number | false | 'async'; 7 | }; 8 | 9 | function decorateFunction unknown>( 10 | fn: T, 11 | options: MemorizeOptions | undefined, 12 | ): T { 13 | return buildIntermediateFunction(fn, options) as ( 14 | ...args: unknown[] 15 | ) => unknown as T; 16 | } 17 | 18 | export function memorize unknown>( 19 | fn: T, 20 | options?: MemorizeOptions, 21 | ): T; 22 | export function memorize(options?: MemorizeOptions): { 23 | unknown>( 24 | target: TMethod, 25 | context: ClassMethodDecoratorContext, 26 | ): TMethod; 27 | unknown>( 28 | target: TGetter, 29 | context: ClassGetterDecoratorContext, 30 | ): TGetter; 31 | }; 32 | export function memorize( 33 | ...args: 34 | | [MemorizeOptions?] 35 | | [(...args: unknown[]) => unknown, MemorizeOptions?] 36 | ): unknown { 37 | if (typeof args[0] === 'function') { 38 | return decorateFunction(args[0], args[1]); 39 | } 40 | 41 | const [options] = args; 42 | 43 | return ( 44 | ...args: 45 | | [ 46 | target: (...args: unknown[]) => unknown, 47 | context: ClassMethodDecoratorContext, 48 | ] 49 | | [target: () => unknown, context: ClassGetterDecoratorContext] 50 | ) => buildIntermediateFunction(args[0], options); 51 | } 52 | 53 | export default memorize; 54 | 55 | function buildIntermediateFunction( 56 | originalFn: (...args: unknown[]) => unknown, 57 | {ttl = Infinity}: MemorizeOptions = {}, 58 | ): (...args: unknown[]) => unknown { 59 | const cacheMap = new MultikeyMap(); 60 | 61 | const name = originalFn.name; 62 | const nameDescriptor = Object.getOwnPropertyDescriptor(fn, 'name')!; 63 | 64 | if (nameDescriptor.configurable) { 65 | Object.defineProperty(fn, 'name', {value: name}); 66 | } else if (nameDescriptor.writable) { 67 | (fn as any).name = name; 68 | } 69 | 70 | return fn; 71 | 72 | function fn(this: any, ...args: any[]): any { 73 | const keys = [this, ...args]; 74 | 75 | let [hasCache, cache] = cacheMap.hasAndGet(keys); 76 | 77 | if (!hasCache) { 78 | cache = originalFn.apply(this, args); 79 | cacheMap.set(keys, cache); 80 | 81 | if (ttl === 'async') { 82 | Promise.resolve(cache).then(cleanUp, cleanUp); 83 | } else if (ttl !== Infinity) { 84 | if (ttl === false) { 85 | RESOLVED.then(cleanUp); 86 | } else { 87 | const timer = setTimeout(cleanUp, ttl) as any; 88 | 89 | if (typeof timer === 'object' && typeof timer.unref === 'function') { 90 | timer.unref(); 91 | } 92 | } 93 | } 94 | } 95 | 96 | return cache; 97 | 98 | function cleanUp(): void { 99 | cacheMap.delete(keys); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/basic.test.ts: -------------------------------------------------------------------------------- 1 | import {jest} from '@jest/globals'; 2 | 3 | import memorize, {memorize as theSameMemorize} from '../library/index.js'; 4 | 5 | describe('exports', () => { 6 | it('should export default', () => { 7 | expect(memorize).toEqual(theSameMemorize); 8 | }); 9 | }); 10 | 11 | describe('getters and methods', () => { 12 | it('should handle getters', () => { 13 | const spy = jest.fn(() => 123); 14 | 15 | class Foo { 16 | @memorize() 17 | get property(): number { 18 | return spy(); 19 | } 20 | } 21 | 22 | const foo = new Foo(); 23 | 24 | expect(spy).not.toHaveBeenCalled; 25 | 26 | expect(foo.property).toEqual(123); 27 | expect(spy).toHaveBeenCalledTimes(1); 28 | 29 | expect(foo.property).toEqual(123); 30 | expect(spy).toHaveBeenCalledTimes(1); 31 | }); 32 | 33 | it('should handle static getters', () => { 34 | const spy = jest.fn(() => 123); 35 | 36 | class Foo { 37 | @memorize() 38 | static get property(): number { 39 | return spy(); 40 | } 41 | } 42 | 43 | expect(spy).not.toHaveBeenCalled; 44 | 45 | expect(Foo.property).toEqual(123); 46 | expect(spy).toHaveBeenCalledTimes(1); 47 | 48 | expect(Foo.property).toEqual(123); 49 | expect(spy).toHaveBeenCalledTimes(1); 50 | }); 51 | 52 | it('should handle methods', () => { 53 | const spy = jest.fn(() => 123); 54 | 55 | class Foo { 56 | @memorize() 57 | method(): number { 58 | return spy(); 59 | } 60 | } 61 | 62 | const foo = new Foo(); 63 | 64 | expect(spy).not.toHaveBeenCalled; 65 | 66 | expect(foo.method()).toEqual(123); 67 | expect(spy).toHaveBeenCalledTimes(1); 68 | 69 | expect(foo.method()).toEqual(123); 70 | expect(spy).toHaveBeenCalledTimes(1); 71 | }); 72 | 73 | it('should handle static methods', () => { 74 | const spy = jest.fn(() => 123); 75 | 76 | class Foo { 77 | @memorize() 78 | static method(): number { 79 | return spy(); 80 | } 81 | } 82 | 83 | expect(spy).not.toHaveBeenCalled; 84 | 85 | expect(Foo.method()).toEqual(123); 86 | expect(spy).toHaveBeenCalledTimes(1); 87 | 88 | expect(Foo.method()).toEqual(123); 89 | expect(spy).toHaveBeenCalledTimes(1); 90 | }); 91 | 92 | it('should handle ttl', async () => { 93 | const spy = jest.fn(() => 123); 94 | 95 | class Foo { 96 | @memorize({ttl: 0}) 97 | get property(): number { 98 | return spy(); 99 | } 100 | } 101 | 102 | const foo = new Foo(); 103 | 104 | expect(spy).not.toHaveBeenCalled; 105 | 106 | expect(foo.property).toEqual(123); 107 | expect(spy).toHaveBeenCalledTimes(1); 108 | 109 | expect(foo.property).toEqual(123); 110 | expect(spy).toHaveBeenCalledTimes(1); 111 | 112 | await new Promise(resolve => setTimeout(resolve, 10)); 113 | 114 | expect(foo.property).toEqual(123); 115 | expect(spy).toHaveBeenCalledTimes(2); 116 | }); 117 | 118 | it('should handle ttl being false', async () => { 119 | const spy = jest.fn(() => 123); 120 | 121 | class Foo { 122 | @memorize({ttl: false}) 123 | get property(): number { 124 | return spy(); 125 | } 126 | } 127 | 128 | const foo = new Foo(); 129 | 130 | expect(spy).not.toHaveBeenCalled; 131 | 132 | expect(foo.property).toEqual(123); 133 | expect(spy).toHaveBeenCalledTimes(1); 134 | 135 | expect(foo.property).toEqual(123); 136 | expect(spy).toHaveBeenCalledTimes(1); 137 | 138 | await new Promise(resolve => setTimeout(resolve, 0)); 139 | 140 | expect(foo.property).toEqual(123); 141 | expect(spy).toHaveBeenCalledTimes(2); 142 | }); 143 | 144 | it('should handle ttl being "async"', async () => { 145 | const values = [123, 456, 789]; 146 | let unstable = true; 147 | 148 | class Foo { 149 | @memorize({ttl: 'async'}) 150 | async getValue(): Promise { 151 | await new Promise(resolve => setTimeout(resolve, 10)); 152 | return values.shift()!; 153 | } 154 | 155 | @memorize({ttl: 'async'}) 156 | async getUnstableValue(): Promise { 157 | await new Promise((resolve, reject) => { 158 | if (unstable) { 159 | reject(); 160 | } else { 161 | setTimeout(resolve, 10); 162 | } 163 | }); 164 | 165 | return values.shift()!; 166 | } 167 | } 168 | 169 | const foo = new Foo(); 170 | 171 | const [a, b] = await Promise.all([foo.getValue(), foo.getValue()]); 172 | 173 | expect(a).toEqual(123); 174 | expect(b).toEqual(123); 175 | 176 | const c = await foo.getValue(); 177 | 178 | expect(c).toEqual(456); 179 | 180 | let e = 0; 181 | let f = 0; 182 | 183 | try { 184 | await Promise.all([foo.getUnstableValue(), foo.getUnstableValue()]); 185 | } catch (err) { 186 | unstable = false; 187 | [e, f] = await Promise.all([ 188 | foo.getUnstableValue(), 189 | foo.getUnstableValue(), 190 | ]); 191 | } 192 | 193 | expect(e).toEqual(789); 194 | expect(f).toEqual(789); 195 | }); 196 | }); 197 | 198 | describe('functions', () => { 199 | it('should handle functions', () => { 200 | const spy = jest.fn(() => 123); 201 | 202 | const fn = memorize(function test() { 203 | return spy(); 204 | }); 205 | 206 | expect(spy).not.toHaveBeenCalled; 207 | 208 | expect(fn()).toEqual(123); 209 | expect((fn as any).name).toEqual('test'); 210 | expect(spy).toHaveBeenCalledTimes(1); 211 | 212 | expect(fn()).toEqual(123); 213 | expect(spy).toHaveBeenCalledTimes(1); 214 | }); 215 | }); 216 | -------------------------------------------------------------------------------- /.magicspace/boilerplate.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$ref": "#/$defs/type-33", 3 | "$defs": { 4 | "type-1": { 5 | "anyOf": [ 6 | { 7 | "type": "string", 8 | "const": "module" 9 | }, 10 | { 11 | "type": "string", 12 | "const": "commonjs" 13 | } 14 | ] 15 | }, 16 | "type-2": { 17 | "anyOf": [ 18 | { 19 | "type": "string", 20 | "const": "Apache-2.0" 21 | }, 22 | { 23 | "type": "string", 24 | "const": "BSD-2-Clause" 25 | }, 26 | { 27 | "type": "string", 28 | "const": "BSD-3-Clause" 29 | }, 30 | { 31 | "type": "string", 32 | "const": "ISC" 33 | }, 34 | { 35 | "type": "string", 36 | "const": "MIT" 37 | }, 38 | { 39 | "type": "string", 40 | "const": "MPL-2.0" 41 | }, 42 | { 43 | "type": "string", 44 | "const": "Unlicense" 45 | }, 46 | { 47 | "type": "string", 48 | "const": "WTFPL" 49 | } 50 | ] 51 | }, 52 | "type-3": { 53 | "type": "object", 54 | "required": [], 55 | "properties": { 56 | "npm": { 57 | "type": "boolean" 58 | }, 59 | "repo": { 60 | "type": "boolean" 61 | }, 62 | "coverage": { 63 | "type": "boolean" 64 | }, 65 | "license": { 66 | "type": "boolean" 67 | }, 68 | "discord": { 69 | "type": "string" 70 | } 71 | }, 72 | "additionalProperties": false 73 | }, 74 | "type-4": { 75 | "anyOf": [ 76 | { 77 | "type": "string", 78 | "const": "pnpm" 79 | }, 80 | { 81 | "type": "string", 82 | "const": "yarn" 83 | } 84 | ] 85 | }, 86 | "type-5": { 87 | "anyOf": [ 88 | { 89 | "type": "string", 90 | "const": "library" 91 | }, 92 | { 93 | "type": "string", 94 | "const": "program" 95 | }, 96 | { 97 | "type": "string", 98 | "const": "script" 99 | } 100 | ] 101 | }, 102 | "type-6": { 103 | "allOf": [ 104 | { 105 | "$ref": "#/$defs/type-5" 106 | } 107 | ], 108 | "description": "Defaults to 'library' if project name includes 'library', otherwise 'program'." 109 | }, 110 | "type-7": { 111 | "type": "object", 112 | "required": [ 113 | "subpath", 114 | "module" 115 | ], 116 | "properties": { 117 | "subpath": { 118 | "type": "string", 119 | "description": "Submodule should be \".\" or relative path that starts with \"./\".", 120 | "pattern": "^\\.(?:\\/.+)?$" 121 | }, 122 | "module": { 123 | "type": "string", 124 | "description": "Module should be the source file name without `.ts` extension relative to the source directory, e.g. \"index\"." 125 | } 126 | }, 127 | "additionalProperties": false 128 | }, 129 | "type-8": { 130 | "anyOf": [ 131 | { 132 | "$ref": "#/$defs/type-7" 133 | }, 134 | { 135 | "type": "string" 136 | }, 137 | { 138 | "type": "boolean" 139 | } 140 | ] 141 | }, 142 | "type-9": { 143 | "allOf": [ 144 | { 145 | "$ref": "#/$defs/type-8" 146 | } 147 | ], 148 | "description": "Whether generate `exports` field in package.json" 149 | }, 150 | "type-10": { 151 | "anyOf": [ 152 | { 153 | "type": "string" 154 | }, 155 | { 156 | "type": "boolean", 157 | "const": false 158 | } 159 | ] 160 | }, 161 | "type-11": { 162 | "allOf": [ 163 | { 164 | "$ref": "#/$defs/type-10" 165 | } 166 | ], 167 | "description": "Extra parent directory, defaults to false." 168 | }, 169 | "type-12": { 170 | "anyOf": [ 171 | { 172 | "type": "string" 173 | }, 174 | { 175 | "type": "boolean", 176 | "const": false 177 | } 178 | ] 179 | }, 180 | "type-13": { 181 | "allOf": [ 182 | { 183 | "$ref": "#/$defs/type-12" 184 | } 185 | ], 186 | "description": "Source directory, defaults to false if the project type is 'script', otherwise 'src'." 187 | }, 188 | "type-14": { 189 | "anyOf": [ 190 | { 191 | "type": "string" 192 | }, 193 | { 194 | "type": "boolean", 195 | "const": false 196 | } 197 | ] 198 | }, 199 | "type-15": { 200 | "allOf": [ 201 | { 202 | "$ref": "#/$defs/type-14" 203 | } 204 | ], 205 | "description": "TypeScript project directory under source directory, defaults to `name` option." 206 | }, 207 | "type-16": { 208 | "type": "array", 209 | "items": { 210 | "type": "string" 211 | } 212 | }, 213 | "type-17": { 214 | "anyOf": [ 215 | { 216 | "$ref": "#/$defs/type-16" 217 | }, 218 | { 219 | "type": "boolean" 220 | } 221 | ] 222 | }, 223 | "type-18": { 224 | "allOf": [ 225 | { 226 | "$ref": "#/$defs/type-17" 227 | } 228 | ], 229 | "description": "Whether to add entrances file(s) and related package/configuration.If true, defaults to ['@entrances.ts']." 230 | }, 231 | "type-19": { 232 | "type": "object", 233 | "required": [ 234 | "package", 235 | "project" 236 | ], 237 | "properties": { 238 | "package": { 239 | "type": "string" 240 | }, 241 | "project": { 242 | "type": "string" 243 | } 244 | }, 245 | "additionalProperties": false 246 | }, 247 | "type-20": { 248 | "anyOf": [ 249 | { 250 | "type": "string" 251 | }, 252 | { 253 | "$ref": "#/$defs/type-19" 254 | } 255 | ] 256 | }, 257 | "type-21": { 258 | "type": "array", 259 | "items": { 260 | "$ref": "#/$defs/type-20" 261 | } 262 | }, 263 | "type-22": { 264 | "allOf": [ 265 | { 266 | "$ref": "#/$defs/type-21" 267 | } 268 | ], 269 | "description": "References to other TypeScript projects, corresponded to `references` field in 'tsconfig.json'" 270 | }, 271 | "type-23": { 272 | "type": "object", 273 | "required": [], 274 | "properties": { 275 | "name": { 276 | "type": "string", 277 | "description": "TypeScript project name, defaults to 'program'." 278 | }, 279 | "type": { 280 | "$ref": "#/$defs/type-6" 281 | }, 282 | "exports": { 283 | "$ref": "#/$defs/type-9" 284 | }, 285 | "exportSourceAs": { 286 | "type": "string", 287 | "description": "Export source with specific condition name, e.g.: 'vite'." 288 | }, 289 | "dev": { 290 | "type": "boolean", 291 | "description": "Whether this TypeScript project is a development-time project, defaults to true if the project name includes 'test' or project type is 'script', otherwise false." 292 | }, 293 | "parentDir": { 294 | "$ref": "#/$defs/type-11" 295 | }, 296 | "src": { 297 | "$ref": "#/$defs/type-13" 298 | }, 299 | "dir": { 300 | "$ref": "#/$defs/type-15" 301 | }, 302 | "noEmit": { 303 | "type": "boolean", 304 | "description": "Whether this project does not emit build artifact, defaults to true if `src` is false, otherwise false." 305 | }, 306 | "test": { 307 | "type": "boolean", 308 | "description": "Whether this project is a test project, defaults to true if `name` includes 'test', otherwise false." 309 | }, 310 | "entrances": { 311 | "$ref": "#/$defs/type-18" 312 | }, 313 | "references": { 314 | "$ref": "#/$defs/type-22" 315 | } 316 | }, 317 | "additionalProperties": false 318 | }, 319 | "type-24": { 320 | "type": "array", 321 | "items": { 322 | "$ref": "#/$defs/type-23" 323 | } 324 | }, 325 | "type-25": { 326 | "type": "object", 327 | "required": [ 328 | "name" 329 | ], 330 | "properties": { 331 | "name": { 332 | "type": "string" 333 | }, 334 | "type": { 335 | "$ref": "#/$defs/type-1" 336 | }, 337 | "dir": { 338 | "type": "string", 339 | "description": "Directory name of this package, defaults to package name (\"@*/\" removed if any)." 340 | }, 341 | "badges": { 342 | "$ref": "#/$defs/type-3" 343 | }, 344 | "projects": { 345 | "$ref": "#/$defs/type-24" 346 | } 347 | }, 348 | "additionalProperties": false 349 | }, 350 | "type-26": { 351 | "type": "array", 352 | "items": { 353 | "$ref": "#/$defs/type-25" 354 | } 355 | }, 356 | "type-27": { 357 | "anyOf": [ 358 | { 359 | "type": "string", 360 | "const": "as-needed" 361 | }, 362 | { 363 | "type": "string", 364 | "const": "consistent" 365 | }, 366 | { 367 | "type": "string", 368 | "const": "preserve" 369 | } 370 | ] 371 | }, 372 | "type-28": { 373 | "anyOf": [ 374 | { 375 | "type": "string", 376 | "const": "es5" 377 | }, 378 | { 379 | "type": "string", 380 | "const": "none" 381 | }, 382 | { 383 | "type": "string", 384 | "const": "all" 385 | } 386 | ] 387 | }, 388 | "type-29": { 389 | "anyOf": [ 390 | { 391 | "type": "string", 392 | "const": "always" 393 | }, 394 | { 395 | "type": "string", 396 | "const": "avoid" 397 | } 398 | ] 399 | }, 400 | "type-30": { 401 | "type": "object", 402 | "required": [ 403 | "printWidth", 404 | "tabWidth", 405 | "useTabs", 406 | "semi", 407 | "singleQuote", 408 | "quoteProps", 409 | "jsxSingleQuote", 410 | "trailingComma", 411 | "bracketSpacing", 412 | "bracketSameLine", 413 | "arrowParens" 414 | ], 415 | "properties": { 416 | "printWidth": { 417 | "type": "number" 418 | }, 419 | "tabWidth": { 420 | "type": "number" 421 | }, 422 | "useTabs": { 423 | "type": "boolean" 424 | }, 425 | "semi": { 426 | "type": "boolean" 427 | }, 428 | "singleQuote": { 429 | "type": "boolean" 430 | }, 431 | "quoteProps": { 432 | "$ref": "#/$defs/type-27" 433 | }, 434 | "jsxSingleQuote": { 435 | "type": "boolean" 436 | }, 437 | "trailingComma": { 438 | "$ref": "#/$defs/type-28" 439 | }, 440 | "bracketSpacing": { 441 | "type": "boolean" 442 | }, 443 | "bracketSameLine": { 444 | "type": "boolean" 445 | }, 446 | "arrowParens": { 447 | "$ref": "#/$defs/type-29" 448 | } 449 | }, 450 | "additionalProperties": false 451 | }, 452 | "type-31": { 453 | "type": "array", 454 | "items": { 455 | "$ref": "#/$defs/type-23" 456 | } 457 | }, 458 | "type-32": { 459 | "type": "object", 460 | "required": [ 461 | "name", 462 | "type", 463 | "defaultBranch", 464 | "packageManager" 465 | ], 466 | "properties": { 467 | "name": { 468 | "type": "string" 469 | }, 470 | "type": { 471 | "$ref": "#/$defs/type-1" 472 | }, 473 | "description": { 474 | "type": "string" 475 | }, 476 | "repository": { 477 | "type": "string" 478 | }, 479 | "license": { 480 | "$ref": "#/$defs/type-2" 481 | }, 482 | "author": { 483 | "type": "string" 484 | }, 485 | "badges": { 486 | "$ref": "#/$defs/type-3" 487 | }, 488 | "defaultBranch": { 489 | "type": "string" 490 | }, 491 | "packageManager": { 492 | "$ref": "#/$defs/type-4" 493 | }, 494 | "packagesDir": { 495 | "type": "string", 496 | "description": "Name of the packages directory, defaults to \"packages\"." 497 | }, 498 | "packages": { 499 | "$ref": "#/$defs/type-26" 500 | }, 501 | "prettier": { 502 | "$ref": "#/$defs/type-30" 503 | }, 504 | "projects": { 505 | "$ref": "#/$defs/type-31" 506 | } 507 | }, 508 | "additionalProperties": false 509 | }, 510 | "type-33": { 511 | "type": "object", 512 | "required": [ 513 | "$schema", 514 | "boilerplate", 515 | "options" 516 | ], 517 | "properties": { 518 | "$schema": { 519 | "type": "string" 520 | }, 521 | "boilerplate": { 522 | "type": "string" 523 | }, 524 | "options": { 525 | "$ref": "#/$defs/type-32" 526 | } 527 | }, 528 | "additionalProperties": false 529 | } 530 | } 531 | } --------------------------------------------------------------------------------