├── .czrc ├── .editorconfig ├── .github ├── actions │ └── publish │ │ ├── Dockerfile │ │ └── entrypoint.sh └── main.workflow ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode └── settings.json ├── README.md ├── docs ├── .gitignore ├── .umirc.js ├── README.md ├── package.json └── src │ ├── author.mdx │ ├── index.mdx │ └── user.mdx ├── examples ├── README.md ├── console-scope.tsmacro │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── console-scope.usage │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── main.ts │ └── tsconfig.json ├── hooks.tsmacro │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── hooks.usage │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── main.tsx │ └── tsconfig.json ├── lowercase.macro │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── babel-lowercase.d.ts │ │ ├── babel-lowercase.js │ │ ├── index.ts │ │ └── typescript-lowercase.ts │ └── tsconfig.json ├── lowercase.usage.js │ ├── .babelrc │ ├── package.json │ ├── rollup.config.js │ └── src │ │ └── main.js ├── lowercase.usage.ts │ ├── package.json │ ├── rollup.config.js │ └── src │ │ └── main.ts ├── transformer-keys.tsmacro │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── transformer-keys.usage │ ├── package.json │ ├── rollup.config.js │ └── src │ │ └── main.ts ├── uppercase.tsmacro │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ └── index.ts │ └── tsconfig.json └── uppercase.usage │ ├── package.json │ ├── rollup.config.js │ └── src │ └── main.ts ├── lerna.json ├── package.json ├── packages ├── interop-export-macros.tsmacro │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── typescript-macros │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ ├── index.ts │ ├── transformerFactoryCreator.ts │ ├── types.ts │ └── utils │ │ ├── getBindingIdentifiers.ts │ │ ├── getMacroImportDeclarations.ts │ │ ├── getUses.ts │ │ └── isMacroImportDeclaration.ts │ └── tsconfig.json ├── tslint.json ├── tsserver-plugin.tsconfig.json └── yarn.lock /.czrc: -------------------------------------------------------------------------------- 1 | { "path": "cz-moe" } 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/actions/publish/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:11-alpine 2 | 3 | LABEL "com.github.actions.name"="GH-Pages Deploy" 4 | LABEL "com.github.actions.description"="Auto Deploy Github Pages" 5 | LABEL "com.github.actions.icon"="upload-cloud" 6 | LABEL "com.github.actions.color"="black" 7 | 8 | LABEL maintainer="ZHAO Jinxiang " 9 | 10 | ADD entrypoint.sh /entrypoint.sh 11 | 12 | 13 | ENTRYPOINT ["/entrypoint.sh"] -------------------------------------------------------------------------------- /.github/actions/publish/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | set -o xtrace 7 | 8 | apk --no-cache add git 9 | git config --global user.email xiaoxiangmoe@gmail.com 10 | git config --global user.name ZHAO Jinxiang 11 | 12 | yarn install --frozen-lockfile 13 | yarn workspace typescript-macros-docs run build 14 | yarn global add gh-pages 15 | gh-pages --dist=docs/.docz/dist 16 | -------------------------------------------------------------------------------- /.github/main.workflow: -------------------------------------------------------------------------------- 1 | workflow "New workflow" { 2 | on = "push" 3 | resolves = ["publish"] 4 | } 5 | 6 | action "Filters for GitHub Actions" { 7 | uses = "actions/bin/filter@e96fd9a" 8 | args = "branch master" 9 | } 10 | 11 | action "publish" { 12 | uses = "./.github/actions/publish" 13 | needs = ["Filters for GitHub Actions"] 14 | secrets = ["GITHUB_TOKEN"] 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules 3 | 4 | # microbundle cache 5 | .rts2_cache_* 6 | .rpt2_cache 7 | .cache 8 | 9 | dist 10 | coverage 11 | 12 | 13 | # Logs 14 | logs 15 | *.log 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "editor.formatOnSave": true, 4 | "prettier.eslintIntegration": true, 5 | "prettier.tslintIntegration": true, 6 | "prettier.stylelintIntegration": true 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Macros 2 | 3 | [![typescript-macros - npm](https://img.shields.io/npm/v/typescript-macros.svg)](https://www.npmjs.com/package/typescript-macros) 4 | 5 | Add macros for TypeScript 6 | 7 | ⚠️ WARNING: This project is still under development 8 | 9 | [docs](https://xiaoxiangmoe.github.io/typescript-macros) 10 | 11 | ## development tutorial 12 | 13 | ```sh 14 | yarn install 15 | yarn build 16 | ``` 17 | 18 | ## TODO 19 | 20 | see [issue: TODO](https://github.com/xiaoxiangmoe/typescript-macros/issues/1) 21 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /.docz 2 | -------------------------------------------------------------------------------- /docs/.umirc.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: [ 3 | [ 4 | 'umi-plugin-library', 5 | { 6 | doc: { 7 | title: 'TypeScript Macros', 8 | base: '.', 9 | hashRouter: true 10 | } 11 | } 12 | ] 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documents for TypeScript Macros 2 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-macros-docs", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "description": "documents for typescript macros", 6 | "authors": { 7 | "name": "ZHAO Jinxiang", 8 | "email": "xiaoxiangmoe@gmail.com" 9 | }, 10 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git/docs", 11 | "scripts": { 12 | "dev": "umi doc dev", 13 | "build": "umi doc build" 14 | }, 15 | "devDependencies": { 16 | "umi": "^2.4.3", 17 | "umi-plugin-library": "^1.0.1" 18 | }, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /docs/src/author.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Usage for macros authors 3 | route: /authors 4 | order: -2 5 | sidebar: true 6 | --- 7 | 8 | # `typescript-macros` Usage for macros authors 9 | 10 | ## Writing a macro 11 | 12 | A macro is a TypeScript module that exports a transform function by another macro(`interop-export-macros.tsmacro`). Here's a simple example: 13 | 14 | ```ts 15 | import { exportTypeScriptMacro } from 'interop-export-macros.tsmacro'; 16 | import * as ts from 'typescript'; 17 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 18 | 19 | /** 20 | * this function will generate function type in`.d.ts` file for us. 21 | * ` declare function uppercase(input: string): string; ` 22 | */ 23 | export function uppercase(input: string): string { 24 | throw new Error('no runtime output'); 25 | } 26 | 27 | const transform: TypeScriptMacroNodeTransformFunction = ({ 28 | reference, 29 | node 30 | }) => { 31 | // convert your node to another node here. 32 | // refrence should input a node and it will return 33 | // * false if this node is not an identifier imported by this macros 34 | // * string if this node is an identifier mported by this macros. 35 | // the string stands for the import name 36 | return node; 37 | }; 38 | 39 | // `exportTypeScriptMacro` is a macro function that generate export function 40 | // we can writing other self-define macro methods to export macros in the future. 41 | exportTypeScriptMacro(transform); 42 | ``` 43 | 44 | ## Filename 45 | 46 | The way that babel-plugin-macros determines whether to run a macro is based on the source string of the import or require statement. 47 | It must match this regex: `/\.(ts)?macro$/` for example: 48 | 49 | - matches: 50 | 51 | ``` 52 | 'my.macro' 53 | 'my.tsmacro' 54 | ``` 55 | 56 | - does not match: 57 | 58 | ``` 59 | 'my-macro' 60 | 'my.macro.is-sweet' 61 | 'my/macro/rocks' 62 | 'my.macro.js' 63 | 'my.macro.ts' 64 | ``` 65 | 66 | maybe we'll support more `my.macro.ts` in the future. 67 | 68 | ## Helpful Tools 69 | 70 | - [tsutils](https://github.com/ajafff/tsutils): Utility functions for working with typescript's AST 71 | - [ts-creator](https://github.com/HearTao/ts-creator): A code generator to generate TypeScript code generator from TypeScript code 72 | 73 | ## Examples 74 | 75 | see https://github.com/xiaoxiangmoe/typescript-macros/tree/master/examples 76 | -------------------------------------------------------------------------------- /docs/src/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Home 3 | route: / 4 | order: 0 5 | sidebar: true 6 | --- 7 | 8 | # TypeScript Macros 🎣 9 | 10 | Enables zero-config, importable typescript macros 11 | 12 | Do not use it now, please. It's under development now. 13 | -------------------------------------------------------------------------------- /docs/src/user.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Usage for users 3 | route: /users 4 | order: -1 5 | sidebar: true 6 | --- 7 | 8 | # `typescript-macros` Usage for users 9 | 10 | ## Configuring rollup 11 | 12 | Adding the transformers to your rollup-plugin-typescript2 13 | 14 | ```js 15 | import typescript from 'rollup-plugin-typescript2'; 16 | import { transformerFactoryCreator } from 'typescript-macros'; 17 | 18 | export default { 19 | ...otherConfig, 20 | plugins: [typescript({ transformers: [transformerFactoryCreator] })] 21 | }; 22 | ``` 23 | 24 | ## Using a macro 25 | 26 | ```ts 27 | import lowercase from '@-.-/lowercase.macro'; 28 | 29 | const ddf = lowercase('DEEP DARK FANTACY!'); 30 | ``` 31 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | This is all examples for debug typescript-macros. 4 | 5 | All packages here is simple and not for production. 6 | 7 | all package's start with scope `@-.-` which is private scope for my own use. 8 | 9 | - `*.tsmacro` shows _how to write a macro_ 10 | - `*.usage` shows _how to use macros_ 11 | -------------------------------------------------------------------------------- /examples/console-scope.tsmacro/README.md: -------------------------------------------------------------------------------- 1 | # @-.-/console-scope.tsmacro 2 | 3 | build 4 | 5 | ```ts 6 | import scope from '@-.-/console-scope.tsmacro'; 7 | export function foo() { 8 | scope(); 9 | const a = 1; 10 | const b = 2; 11 | } 12 | 13 | export function bar() { 14 | const a = 1; 15 | scope(); 16 | const b = 2; 17 | } 18 | ``` 19 | 20 | to 21 | 22 | ```js 23 | function foo() { 24 | console.log('begin scope:foo'); 25 | const a = 1; 26 | console.log('a<--', a); 27 | const b = 2; 28 | console.log('b<--', b); 29 | } 30 | 31 | function bar() { 32 | console.log('begin scope:bar'); 33 | const a = 1; 34 | console.log('a<--', a); 35 | const b = 2; 36 | console.log('b<--', b); 37 | } 38 | 39 | export { foo, bar }; 40 | ``` 41 | -------------------------------------------------------------------------------- /examples/console-scope.tsmacro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/console-scope.tsmacro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "rollup --config=rollup.config.js", 14 | "postbuild": "rm -rf .rpt2_cache" 15 | }, 16 | "dependencies": { 17 | "typescript": "^3.2.4", 18 | "typescript-macros": "^0.0.1-alpha.0" 19 | }, 20 | "devDependencies": { 21 | "interop-export-macros.tsmacro": "^0.0.1-alpha.0", 22 | "rollup": "^1.1.2", 23 | "rollup-plugin-typescript2": "^0.19.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/console-scope.tsmacro/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | 3 | import { transformerFactoryCreator } from 'typescript-macros'; 4 | 5 | export default { 6 | input: 'src/index.ts', 7 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 8 | output: [ 9 | { 10 | file: 'dist/index.js', 11 | format: 'cjs', 12 | exports: 'named', 13 | interop: true 14 | }, 15 | { 16 | file: 'dist/index.mjs', 17 | format: 'esm' 18 | } 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /examples/console-scope.tsmacro/src/index.ts: -------------------------------------------------------------------------------- 1 | import { exportTypeScriptMacro } from 'interop-export-macros.tsmacro'; 2 | import * as ts from 'typescript'; 3 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 4 | 5 | function scope(): void { 6 | throw new Error('no runtime output'); 7 | } 8 | 9 | // warning: if we use export default, It will be ignored because export default if design for babel-plugin-macros 10 | export { scope as default }; 11 | 12 | const transform: TypeScriptMacroNodeTransformFunction = ({ 13 | reference, 14 | node, 15 | }) => { 16 | const isScopeStatement = (x: ts.Statement) => 17 | ts.isExpressionStatement(x) && 18 | ts.isCallExpression(x.expression) && 19 | ts.isIdentifier(x.expression.expression) && 20 | reference(x.expression.expression) === 'default'; 21 | 22 | const isAssignStatement = (x: ts.Statement): x is ts.VariableStatement => { 23 | if (!ts.isVariableStatement(x)) { 24 | return false; 25 | } 26 | if (x.declarationList.declarations.length !== 1) { 27 | return false; 28 | } 29 | const node = x.declarationList.declarations[0]; 30 | return true; 31 | }; 32 | 33 | const createAstFromString = (input: string) => 34 | (ts.createSourceFile('temp.ts', input, node.getSourceFile().languageVersion) 35 | .statements[0] as ts.ExpressionStatement).expression; 36 | 37 | if ( 38 | ts.isFunctionDeclaration(node) && 39 | node.body !== undefined && 40 | node.body.statements.some(isScopeStatement) 41 | ) { 42 | return ts.createFunctionDeclaration( 43 | node.decorators, 44 | node.modifiers, 45 | node.asteriskToken, 46 | node.name, 47 | node.typeParameters, 48 | node.parameters, 49 | node.type, 50 | ts.createBlock([ 51 | ts.createExpressionStatement( 52 | createAstFromString( 53 | `console.log('begin scope:${ 54 | node.name ? node.name.text : 'anonymous' 55 | }')`, 56 | ), 57 | ), 58 | ...node.body.statements.flatMap(x => 59 | isScopeStatement(x) 60 | ? [] 61 | : isAssignStatement(x) 62 | ? [ 63 | x, 64 | ts.createExpressionStatement( 65 | createAstFromString( 66 | `console.log('${x.declarationList.declarations[0].name.getText()} <--',${x.declarationList.declarations[0].name.getText()})`, 67 | ), 68 | ), 69 | ] 70 | : [x], 71 | ), 72 | ]), 73 | ); 74 | } 75 | 76 | return node; 77 | }; 78 | 79 | exportTypeScriptMacro(transform); 80 | -------------------------------------------------------------------------------- /examples/console-scope.tsmacro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "lib": ["esnext"], 5 | "declaration": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/console-scope.usage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/console-scope.usage", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/console-scope.tsmacro": "^0.0.1-alpha.0", 12 | "rollup": "^1.1.2", 13 | "rollup-plugin-typescript2": "^0.19.2", 14 | "typescript": "^3.2.4", 15 | "typescript-macros": "^0.0.1-alpha.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/console-scope.usage/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 4 | // import tsMacro from './src/typescript-plugin-macros'; 5 | 6 | import {transformerFactoryCreator} from 'typescript-macros'; 7 | 8 | export default { 9 | input: 'src/main.ts', 10 | plugins: [ 11 | typescript({ transformers: [transformerFactoryCreator] }) 12 | ], 13 | output: { 14 | file: 'dist/main.js', 15 | format: 'es', 16 | indent: true, 17 | }, 18 | treeshake:false, 19 | }; 20 | -------------------------------------------------------------------------------- /examples/console-scope.usage/src/main.ts: -------------------------------------------------------------------------------- 1 | import scope from '@-.-/console-scope.tsmacro'; 2 | export function foo() { 3 | scope(); 4 | const a = 1; 5 | const b = 2; 6 | } 7 | 8 | export function bar() { 9 | const a = 1; 10 | scope(); 11 | const b = 2; 12 | } 13 | -------------------------------------------------------------------------------- /examples/console-scope.usage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "strict": true, 5 | "lib": ["esnext"] 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /examples/hooks.tsmacro/README.md: -------------------------------------------------------------------------------- 1 | # @-.-/hooks.tsmacro 2 | 3 | build 4 | 5 | ```tsx 6 | import { useAutoMemo } from '@-.-/hooks.tsmacro'; 7 | import * as React from 'react'; 8 | 9 | export const MyComponent: React.FC<{ labels: Array }> = ({ 10 | labels 11 | }) => { 12 | const myComputation = useAutoMemo(() => 13 | labels.map(label => label.toUpperCase()) 14 | ); 15 | return
{JSON.stringify(myComputation)}
; 16 | }; 17 | ``` 18 | 19 | to 20 | 21 | ```js 22 | import { useMemo, createElement } from 'react'; 23 | 24 | const MyComponent = ({ labels }) => { 25 | const myComputation = useMemo( 26 | () => labels.map(label => label.toUpperCase()), 27 | [labels] 28 | ); 29 | return createElement('div', null, JSON.stringify(myComputation)); 30 | }; 31 | 32 | export { MyComponent }; 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/hooks.tsmacro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/hooks.tsmacro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "rollup --config=rollup.config.js", 14 | "postbuild": "rm -rf .rpt2_cache" 15 | }, 16 | "dependencies": { 17 | "tsutils": "^3.5.1", 18 | "typescript": "^3.2.4", 19 | "typescript-macros": "^0.0.1-alpha.0" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^16.7.20", 23 | "interop-export-macros.tsmacro": "^0.0.1-alpha.0", 24 | "react": "^16.8.0-alpha.1", 25 | "react-dom": "^16.8.0-alpha.1", 26 | "rollup": "^1.1.2", 27 | "rollup-plugin-typescript2": "^0.19.2" 28 | }, 29 | "peerDependencies": { 30 | "react": "^16.8.0-alpha.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/hooks.tsmacro/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | 3 | import { transformerFactoryCreator } from 'typescript-macros'; 4 | 5 | export default { 6 | input: 'src/index.ts', 7 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 8 | output: [ 9 | { 10 | file: 'dist/index.js', 11 | format: 'cjs', 12 | exports: 'named', 13 | interop: true 14 | }, 15 | { 16 | file: 'dist/index.mjs', 17 | format: 'esm' 18 | } 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /examples/hooks.tsmacro/src/index.ts: -------------------------------------------------------------------------------- 1 | import { exportTypeScriptMacro } from 'interop-export-macros.tsmacro'; 2 | import { useMemo } from 'react'; 3 | import { collectVariableUsage } from 'tsutils'; 4 | import * as ts from 'typescript'; 5 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 6 | 7 | export function useAutoMemo(factory: () => T) { 8 | console.error('Can\'t use useAutoMemo function in runtime'); 9 | return useMemo(factory, []); 10 | } 11 | 12 | const transform: TypeScriptMacroNodeTransformFunction = ({ 13 | reference, 14 | node, 15 | }) => { 16 | if ( 17 | ts.isCallExpression(node) && 18 | ts.isIdentifier(node.expression) && 19 | reference(node.expression) === useAutoMemo.name 20 | ) { 21 | if (node.arguments.length !== 1) { 22 | throw new Error('arguments\'s length should be 1'); 23 | } 24 | const factory = node.arguments[0]; 25 | if (!(ts.isArrowFunction(factory) || ts.isFunctionExpression(factory))) { 26 | throw new Error('factory should be FunctionExpression or ArrowFunction'); 27 | } 28 | 29 | const createAstFromString = (input: string) => 30 | (ts.createSourceFile( 31 | 'temp.ts', 32 | input, 33 | node.getSourceFile().languageVersion, 34 | ).statements[0] as ts.ExpressionStatement).expression; 35 | 36 | const allVariables = Array.from( 37 | collectVariableUsage(node.getSourceFile()).entries(), 38 | ); 39 | 40 | const isParent = (node: ts.Node, maybeParentNode: ts.Node): boolean => 41 | node.parent === undefined 42 | ? false 43 | : node.parent === maybeParentNode 44 | ? true 45 | : isParent(node.parent, maybeParentNode); 46 | const freeVariables = allVariables 47 | .filter( 48 | ([declare, info]) => 49 | !isParent(declare, factory) && 50 | info.uses.some(x => isParent(x.location, factory)), 51 | ) 52 | .map(([declare, info]) => declare.text); 53 | 54 | const a = createAstFromString( 55 | `React.useMemo(null,[${freeVariables.join(',')}]) `, 56 | ) as ts.CallExpression; 57 | 58 | return ts.createCall(a.expression, node.typeArguments, [ 59 | node.arguments[0], 60 | a.arguments[1], 61 | ]); 62 | } 63 | return node; 64 | }; 65 | 66 | exportTypeScriptMacro(transform); 67 | -------------------------------------------------------------------------------- /examples/hooks.tsmacro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "lib": ["esnext"], 5 | "declaration": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/hooks.usage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/hooks.usage", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/hooks.tsmacro": "^0.0.1-alpha.0", 12 | "@types/react": "^16.7.20", 13 | "rollup": "^1.1.2", 14 | "rollup-plugin-typescript2": "^0.19.2", 15 | "typescript": "^3.2.4", 16 | "typescript-macros": "^0.0.1-alpha.0" 17 | }, 18 | "dependencies": { 19 | "react": "^16.8.0-alpha.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/hooks.usage/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 4 | // import tsMacro from './src/typescript-plugin-macros'; 5 | 6 | import {transformerFactoryCreator} from 'typescript-macros'; 7 | 8 | export default { 9 | input: 'src/main.tsx', 10 | plugins: [ 11 | typescript({ transformers: [transformerFactoryCreator] }) 12 | ], 13 | output: { 14 | file: 'dist/main.js', 15 | format: 'es', 16 | indent: true, 17 | }, 18 | treeshake:false, 19 | }; 20 | -------------------------------------------------------------------------------- /examples/hooks.usage/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { useAutoMemo } from '@-.-/hooks.tsmacro'; 2 | import * as React from 'react'; 3 | 4 | export const MyComponent: React.FC<{ readonly labels: string[] }> = ({ 5 | labels, 6 | }) => { 7 | const myComputation = useAutoMemo(() => 8 | labels.map(label => label.toUpperCase()), 9 | ); 10 | return
{JSON.stringify(myComputation)}
; 11 | }; 12 | -------------------------------------------------------------------------------- /examples/hooks.usage/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "strict": false, 5 | "lib": ["esnext"], 6 | "jsx": "react" 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /examples/lowercase.macro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/lowercase.macro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "rollup --config=rollup.config.js", 14 | "postbuild": "rm -rf .rpt2_cache" 15 | }, 16 | "dependencies": { 17 | "babel-plugin-macros": "^2.4.5", 18 | "typescript": "^3.2.4", 19 | "typescript-macros": "^0.0.1-alpha.0" 20 | }, 21 | "devDependencies": { 22 | "interop-export-macros.tsmacro": "^0.0.1-alpha.0", 23 | "rollup": "^1.1.2", 24 | "rollup-plugin-typescript2": "^0.19.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/lowercase.macro/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | 4 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 5 | // import tsMacro from './src/typescript-plugin-macros'; 6 | 7 | import { transformerFactoryCreator } from 'typescript-macros'; 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 12 | output: [ 13 | { 14 | file: 'dist/index.js', 15 | format: 'cjs', 16 | exports: 'named', 17 | interop: true 18 | }, 19 | { 20 | file: 'dist/index.mjs', 21 | format: 'esm' 22 | } 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /examples/lowercase.macro/src/babel-lowercase.d.ts: -------------------------------------------------------------------------------- 1 | declare const createMacro: any; 2 | export default createMacro; 3 | -------------------------------------------------------------------------------- /examples/lowercase.macro/src/babel-lowercase.js: -------------------------------------------------------------------------------- 1 | const { createMacro } = require('babel-plugin-macros'); 2 | function lowercaseMacro({ references, state, babel, config }) { 3 | references.default.forEach(referencePath => { 4 | if (referencePath.parentPath.type !== 'CallExpression') { 5 | throw new Error('Not allowed'); 6 | } 7 | 8 | const t = babel.types; 9 | 10 | const args = referencePath.parentPath.node.arguments; 11 | if (args.length !== 1) { 12 | throw new Error("args's length should be 1"); 13 | } 14 | const arg = args[0]; 15 | 16 | if (arg.type !== 'StringLiteral') { 17 | throw new Error('only allow StringLiteral as arg'); 18 | } 19 | 20 | referencePath.parentPath.replaceWith( 21 | t.stringLiteral(arg.value.toLowerCase()) 22 | ); 23 | }); 24 | } 25 | export default createMacro(lowercaseMacro); 26 | 27 | -------------------------------------------------------------------------------- /examples/lowercase.macro/src/index.ts: -------------------------------------------------------------------------------- 1 | import babelFunc from './babel-lowercase'; 2 | import tsFunc from './typescript-lowercase'; 3 | 4 | import { 5 | exportBabelMacro, 6 | exportTypeScriptMacro, 7 | } from 'interop-export-macros.tsmacro'; 8 | 9 | /** 10 | * lowercase input string 11 | * @param input only a StringLiteral 12 | * @returns lowercased StringLiteral 13 | */ 14 | declare function lowercase(input: string): string; 15 | 16 | // export default will be ignored in generated js file 17 | // but it will still work in .d.ts file 18 | export default lowercase; 19 | 20 | exportTypeScriptMacro(tsFunc); 21 | exportBabelMacro(babelFunc); 22 | -------------------------------------------------------------------------------- /examples/lowercase.macro/src/typescript-lowercase.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 3 | 4 | const transformFunction: TypeScriptMacroNodeTransformFunction = ({ 5 | reference, 6 | node, 7 | }) => { 8 | if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { 9 | const refName = reference(node.expression); 10 | if (refName !== false) { 11 | if (refName !== 'default') { 12 | throw Error('only allow default export'); 13 | } 14 | if (node.arguments.length !== 1) { 15 | throw Error('only allow 1 param'); 16 | } 17 | const param = node.arguments[0]; 18 | if (!ts.isStringLiteralLike(param)) { 19 | throw Error('only allow StringLiteral & NoSubstitutionTemplateLiteral'); 20 | } 21 | return ts.createStringLiteral(param.text.toLowerCase()); 22 | } 23 | } 24 | 25 | return node; 26 | }; 27 | 28 | export default transformFunction; 29 | -------------------------------------------------------------------------------- /examples/lowercase.macro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "strict": true, 5 | "lib": ["esnext"], 6 | "declaration": true, 7 | "esModuleInterop": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/lowercase.usage.js/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["macros"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/lowercase.usage.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/lowercase.usage.js", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/lowercase.macro": "^0.0.1-alpha.0", 12 | "@babel/core": "^7.2.2", 13 | "babel-plugin-macros": "^2.4.5", 14 | "babel-plugin-preval": "^3.0.1", 15 | "rollup": "^1.1.2", 16 | "rollup-plugin-babel": "^4.3.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/lowercase.usage.js/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 3 | // import tsMacro from './src/typescript-plugin-macros'; 4 | import babel from 'rollup-plugin-babel'; 5 | 6 | export default { 7 | input: 'src/main.js', 8 | plugins: [ 9 | babel({ 10 | exclude: 'node_modules/**' 11 | }) 12 | ], 13 | output: { 14 | file: 'dist/main.js', 15 | format: 'esm' 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /examples/lowercase.usage.js/src/main.js: -------------------------------------------------------------------------------- 1 | import lowercase from '@-.-/lowercase.macro'; 2 | 3 | export const van = lowercase( 4 | "My name is Van. Uh...I'm an artist. I'm a performance artist." 5 | ); 6 | -------------------------------------------------------------------------------- /examples/lowercase.usage.ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/lowercase.usage.ts", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/lowercase.macro": "^0.0.1-alpha.0", 12 | "rollup": "^1.1.2", 13 | "rollup-plugin-typescript2": "^0.19.2", 14 | "typescript": "^3.2.4", 15 | "typescript-macros": "^0.0.1-alpha.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/lowercase.usage.ts/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 4 | // import tsMacro from './src/typescript-plugin-macros'; 5 | 6 | import { transformerFactoryCreator } from 'typescript-macros'; 7 | 8 | export default { 9 | input: 'src/main.ts', 10 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 11 | output: { 12 | file: 'dist/main.js', 13 | format: 'esm' 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /examples/lowercase.usage.ts/src/main.ts: -------------------------------------------------------------------------------- 1 | import lowercase from '@-.-/lowercase.macro'; 2 | export function foo() { 3 | return lowercase('DEEP DARK FANTACY!'); 4 | } 5 | -------------------------------------------------------------------------------- /examples/transformer-keys.tsmacro/README.md: -------------------------------------------------------------------------------- 1 | # @-.-/transformer-keys.usage 2 | 3 | build 4 | 5 | ```ts 6 | import { keys } from '@-.-/transformer-keys.tsmacro'; 7 | 8 | interface Props { 9 | readonly id: string; 10 | readonly name: string; 11 | readonly age: number; 12 | } 13 | const keysOfProps = keys(); 14 | 15 | console.log(keysOfProps); // ['id', 'name', 'age'] 16 | ``` 17 | 18 | to 19 | 20 | ```js 21 | var keysOfProps = ['id', 'name', 'age']; 22 | console.log(keysOfProps); // ['id', 'name', 'age'] 23 | ``` 24 | 25 | see: [kimamula/ts-transformer-keys](https://github.com/kimamula/ts-transformer-keys) 26 | -------------------------------------------------------------------------------- /examples/transformer-keys.tsmacro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/transformer-keys.tsmacro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "rollup --config=rollup.config.js", 14 | "postbuild": "rm -rf .rpt2_cache" 15 | }, 16 | "dependencies": { 17 | "typescript": "^3.2.4", 18 | "typescript-macros": "^0.0.1-alpha.0" 19 | }, 20 | "devDependencies": { 21 | "interop-export-macros.tsmacro": "^0.0.1-alpha.0", 22 | "rollup": "^1.1.2", 23 | "rollup-plugin-typescript2": "^0.19.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/transformer-keys.tsmacro/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | 3 | import { transformerFactoryCreator } from 'typescript-macros'; 4 | 5 | export default { 6 | input: 'src/index.ts', 7 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 8 | output: [ 9 | { 10 | file: 'dist/index.js', 11 | format: 'cjs', 12 | exports: 'named', 13 | interop: true 14 | }, 15 | { 16 | file: 'dist/index.mjs', 17 | format: 'esm' 18 | } 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /examples/transformer-keys.tsmacro/src/index.ts: -------------------------------------------------------------------------------- 1 | import { exportTypeScriptMacro } from 'interop-export-macros.tsmacro'; 2 | import * as ts from 'typescript'; 3 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 4 | 5 | export function keys(): (keyof T)[] { 6 | console.error(new Error('no runtime output')); 7 | return []; 8 | } 9 | 10 | const transform: TypeScriptMacroNodeTransformFunction = ({ 11 | reference, 12 | languageService, 13 | node, 14 | }) => { 15 | if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { 16 | const refName = reference(node.expression); 17 | if (refName !== false) { 18 | if (refName !== (keys as any).name) { 19 | // FIXME: Functions should have a property 'name'. remove this any until TS support this. 20 | throw Error(`no ${refName} export`); 21 | } 22 | if (node.arguments.length !== 0) { 23 | throw Error('only allow 0 param'); 24 | } 25 | const typeChecker = languageService.getProgram()!.getTypeChecker(); 26 | if (!node.typeArguments) { 27 | return ts.createArrayLiteral([]); 28 | } 29 | const type = typeChecker.getTypeFromTypeNode(node.typeArguments[0]); 30 | const properties = typeChecker.getPropertiesOfType(type); 31 | return ts.createArrayLiteral( 32 | properties.map(property => ts.createLiteral(property.name)), 33 | ); 34 | } 35 | } 36 | return node; 37 | }; 38 | 39 | exportTypeScriptMacro(transform); 40 | -------------------------------------------------------------------------------- /examples/transformer-keys.tsmacro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "lib": ["esnext"], 5 | "declaration": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/transformer-keys.usage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/transformer-keys.usage", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/transformer-keys.tsmacro": "^0.0.1-alpha.0", 12 | "rollup": "^1.1.2", 13 | "rollup-plugin-typescript2": "^0.19.2", 14 | "typescript": "^3.2.4", 15 | "typescript-macros": "^0.0.1-alpha.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/transformer-keys.usage/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 4 | // import tsMacro from './src/typescript-plugin-macros'; 5 | 6 | import {transformerFactoryCreator} from 'typescript-macros'; 7 | 8 | export default { 9 | input: 'src/main.ts', 10 | plugins: [ 11 | typescript({ transformers: [transformerFactoryCreator] }) 12 | ], 13 | output: { 14 | file: 'dist/main.js', 15 | format: 'es' 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /examples/transformer-keys.usage/src/main.ts: -------------------------------------------------------------------------------- 1 | import { keys } from '@-.-/transformer-keys.tsmacro'; 2 | 3 | interface Props { 4 | readonly id: string; 5 | readonly name: string; 6 | readonly age: number; 7 | } 8 | const keysOfProps = keys(); 9 | 10 | console.log(keysOfProps); // ['id', 'name', 'age'] 11 | -------------------------------------------------------------------------------- /examples/uppercase.tsmacro/README.md: -------------------------------------------------------------------------------- 1 | # @-.-/uppercase.tsmacro 2 | 3 | build 4 | 5 | ```ts 6 | import uppercase from '@-.-/uppercase.tsmacro'; 7 | export function foo() { 8 | return uppercase('Hello world!'); 9 | } 10 | ``` 11 | 12 | to 13 | 14 | ```js 15 | function foo() { 16 | return 'HELLO WORLD!'; 17 | } 18 | 19 | export { foo }; 20 | ``` 21 | -------------------------------------------------------------------------------- /examples/uppercase.tsmacro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/uppercase.tsmacro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "rollup --config=rollup.config.js", 14 | "postbuild": "rm -rf .rpt2_cache" 15 | }, 16 | "dependencies": { 17 | "typescript": "^3.2.4", 18 | "typescript-macros": "^0.0.1-alpha.0" 19 | }, 20 | "devDependencies": { 21 | "interop-export-macros.tsmacro": "^0.0.1-alpha.0", 22 | "microbundle": "^0.9.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/uppercase.tsmacro/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | 3 | import { transformerFactoryCreator } from 'typescript-macros'; 4 | 5 | export default { 6 | input: 'src/index.ts', 7 | plugins: [typescript({ transformers: [transformerFactoryCreator] })], 8 | output: [ 9 | { 10 | file: 'dist/index.js', 11 | format: 'cjs', 12 | exports: 'named', 13 | interop: true 14 | }, 15 | { 16 | file: 'dist/index.mjs', 17 | format: 'esm' 18 | } 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /examples/uppercase.tsmacro/src/index.ts: -------------------------------------------------------------------------------- 1 | import { exportTypeScriptMacro } from 'interop-export-macros.tsmacro'; 2 | import * as ts from 'typescript'; 3 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 4 | 5 | /** 6 | * uppercase input string 7 | * @param input only a StringLiteral 8 | * @returns uppercased StringLiteral 9 | */ 10 | function uppercase(input: string): string { 11 | console.error(new Error('no runtime output')); 12 | return input.toUpperCase(); 13 | } 14 | 15 | export { uppercase as default }; 16 | 17 | const transform: TypeScriptMacroNodeTransformFunction = ({ 18 | reference, 19 | node, 20 | }) => { 21 | if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { 22 | const refName = reference(node.expression); 23 | if (refName !== false) { 24 | if (refName !== 'default') { 25 | throw Error('only allow default export'); 26 | } 27 | if (node.arguments.length !== 1) { 28 | throw Error('only allow 1 param'); 29 | } 30 | const param = node.arguments[0]; 31 | if (!ts.isStringLiteralLike(param)) { 32 | throw Error('only allow StringLiteral & NoSubstitutionTemplateLiteral'); 33 | } 34 | return ts.createStringLiteral(param.text.toUpperCase()); 35 | } 36 | } 37 | return node; 38 | }; 39 | 40 | exportTypeScriptMacro(transform); 41 | -------------------------------------------------------------------------------- /examples/uppercase.tsmacro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "lib": ["esnext"], 5 | "declaration": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/uppercase.usage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@-.-/uppercase.usage", 3 | "version": "0.0.1-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rollup --config=rollup.config.js", 8 | "postbuild": "rm -rf .rpt2_cache" 9 | }, 10 | "devDependencies": { 11 | "@-.-/uppercase.tsmacro": "^0.0.1-alpha.0", 12 | "rollup": "^1.1.2", 13 | "rollup-plugin-typescript2": "^0.19.2", 14 | "typescript": "^3.2.4", 15 | "typescript-macros": "^0.0.1-alpha.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/uppercase.usage/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from 'rollup-plugin-typescript2'; 3 | // const keysTransformer = require('ts-transformer-keys/transformer').default; 4 | // import tsMacro from './src/typescript-plugin-macros'; 5 | 6 | import {transformerFactoryCreator} from 'typescript-macros'; 7 | 8 | export default { 9 | input: 'src/main.ts', 10 | plugins: [ 11 | typescript({ transformers: [transformerFactoryCreator] }) 12 | ], 13 | output: { 14 | file: 'dist/main.js', 15 | format: 'es' 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /examples/uppercase.usage/src/main.ts: -------------------------------------------------------------------------------- 1 | import uppercase from '@-.-/uppercase.tsmacro'; 2 | export function foo() { 3 | return uppercase('Hello world!'); 4 | } 5 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "useWorkspaces": true, 4 | "version": "independent" 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "lerna": "lerna", 5 | "publish": "lerna publish", 6 | "precommit": "yarn lint", 7 | "commit": "git-cz", 8 | "test": "echo 'test is just build in this project' && yarn run build", 9 | "build": "yarn workspaces run build", 10 | "lint": "yarn run tslint", 11 | "tslint": "tslint --config tslint.json packages/*/src/**/*.ts" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^23.3.10", 15 | "commitizen": "^3.0.5", 16 | "cz-moe": "^0.0.2", 17 | "jest": "^23.6.0", 18 | "lerna": "^3.5.0", 19 | "ts-jest": "^23.10.5", 20 | "tslint": "^5.11.0", 21 | "tslint-immutable": "^5.0.0", 22 | "tslint-microsoft-contrib": "^5.2.1", 23 | "typescript": "^3.2.4", 24 | "typescript-tslint-plugin": "^0.1.2" 25 | }, 26 | "workspaces": [ 27 | "packages/typescript-macros", 28 | "packages/interop-export-macros.tsmacro", 29 | "examples/*", 30 | "docs" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/interop-export-macros.tsmacro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interop-export-macros.tsmacro", 3 | "private": true, 4 | "version": "0.0.1-alpha.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "module": "dist/index.mjs", 8 | "source": "src/index.ts", 9 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 10 | "author": "ZHAO Jinxiang ", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": " microbundle build --no-compress --strict --format=cjs,es && sed -i '' '/____$$$$____typescriptMacroNodeTransformFunction____$$$$____/d' ./dist/index.d.ts", 14 | "postbuild": "rm -rf .rts2_cache_*" 15 | }, 16 | "dependencies": { 17 | "typescript": "^3.2.4", 18 | "typescript-macros": "^0.0.1-alpha.0" 19 | }, 20 | "devDependencies": { 21 | "microbundle": "^0.9.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/interop-export-macros.tsmacro/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { TypeScriptMacroNodeTransformFunction } from 'typescript-macros'; 3 | 4 | /** 5 | * It will export babel macro for users 6 | * @param babelMacroFunction 7 | */ 8 | function exportBabelMacro(babelMacroFunction: Function): void {} 9 | 10 | /** 11 | * It will export typescript macro for users 12 | * @param tsMacroFunction 13 | */ 14 | function exportTypeScriptMacro( 15 | tsMacroFunction: TypeScriptMacroNodeTransformFunction, 16 | ): void {} 17 | 18 | export { exportBabelMacro, exportTypeScriptMacro }; 19 | 20 | export const ____$$$$____typescriptMacroNodeTransformFunction____$$$$____: TypeScriptMacroNodeTransformFunction = ({ 21 | reference, 22 | node, 23 | }) => { 24 | if (ts.isSourceFile(node.parent)) { 25 | if (ts.isExportAssignment(node)) { 26 | return ts.createEmptyStatement(); 27 | } 28 | 29 | if ( 30 | ts.isExpressionStatement(node) && 31 | ts.isCallExpression(node.expression) && 32 | ts.isIdentifier(node.expression.expression) && 33 | ['exportTypeScriptMacro', 'exportBabelMacro'].includes(reference( 34 | node.expression.expression, 35 | ) as string) 36 | ) { 37 | const args = node.expression.arguments; 38 | if (args.length !== 1) { 39 | throw new Error('only allow 1 arg'); 40 | } 41 | const arg = args[0]; 42 | if (!ts.isIdentifier(arg)) { 43 | throw new Error('only allow Identifier in arg'); 44 | } 45 | 46 | return reference(node.expression.expression) === 'exportTypeScriptMacro' 47 | ? ts.createExportDeclaration( 48 | [], 49 | [], 50 | ts.createNamedExports([ 51 | ts.createExportSpecifier( 52 | arg.text, 53 | '____$$$$____typescriptMacroNodeTransformFunction____$$$$____', 54 | ), 55 | ]), 56 | ) 57 | : ts.createExportAssignment( 58 | [], 59 | [], 60 | false, 61 | ts.createIdentifier(arg.text), 62 | ); 63 | } 64 | } 65 | 66 | return node; 67 | }; 68 | -------------------------------------------------------------------------------- /packages/interop-export-macros.tsmacro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "strict": true, 5 | "lib": ["esnext"], 6 | "declaration": true, 7 | "esModuleInterop": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/typescript-macros/.npmignore: -------------------------------------------------------------------------------- 1 | 2 | .rts2_cache_* 3 | -------------------------------------------------------------------------------- /packages/typescript-macros/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Macros 2 | 3 | [![typescript-macros - npm](https://img.shields.io/npm/v/typescript-macros.svg)](https://www.npmjs.com/package/typescript-macros) 4 | 5 | Add macros for TypeScript 6 | 7 | ⚠️ WARNING: This project is still under development 8 | -------------------------------------------------------------------------------- /packages/typescript-macros/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-macros", 3 | "version": "0.0.1-alpha.0", 4 | "main": "dist/index.js", 5 | "types": "dist/index.d.ts", 6 | "module": "dist/index.mjs", 7 | "source": "src/index.ts", 8 | "repository": "https://github.com/xiaoxiangmoe/typescript-macros.git", 9 | "author": "ZHAO Jinxiang ", 10 | "license": "MIT", 11 | "scripts": { 12 | "build": " microbundle build --no-compress --strict --format=cjs,es", 13 | "postbuild": "rm -rf .rts2_cache_*" 14 | }, 15 | "dependencies": { 16 | "tsutils": "^3.7.0", 17 | "typescript": "^3.2.4" 18 | }, 19 | "devDependencies": { 20 | "microbundle": "^0.9.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Redirects 3 | */ 4 | 5 | export { transformerFactoryCreator } from './transformerFactoryCreator'; 6 | export * from './types'; 7 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/transformerFactoryCreator.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable: no-shadowed-variable missing-jsdoc 2 | 3 | import * as ts from 'typescript'; 4 | import { 5 | TransformerFactoryCreator, 6 | TypeScriptMacroNodeTransformFunction, 7 | } from './types'; 8 | import { 9 | getBindingIdentifiers, 10 | IdentifiersMap, 11 | } from './utils/getBindingIdentifiers'; 12 | import { getMacroImportDeclarations } from './utils/getMacroImportDeclarations'; 13 | import { isMacroImportDeclaration } from './utils/isMacroImportDeclaration'; 14 | 15 | export const transformerFactoryCreator: TransformerFactoryCreator = languageService => { 16 | const beforeTransformerFactory: ts.TransformerFactory< 17 | ts.SourceFile 18 | > = context => node => { 19 | const visitEachChild = (node: T, visitor: ts.Visitor) => 20 | ts.visitEachChild(node, visitor, context); 21 | 22 | const visitorFactory = ({ 23 | bindings, 24 | transFunc, 25 | }: { 26 | readonly bindings: IdentifiersMap; 27 | readonly transFunc: TypeScriptMacroNodeTransformFunction; 28 | }) => { 29 | const visitor: ts.Visitor = node => { 30 | if (ts.isImportDeclaration(node)) { 31 | return isMacroImportDeclaration(node) ? undefined : node; 32 | } 33 | const ret = transFunc({ 34 | reference(node) { 35 | const a = Object.entries(bindings).find(([key, ids]) => 36 | ids.includes(node), 37 | ); 38 | return a === undefined ? false : a[0]; 39 | }, 40 | node, 41 | languageService, 42 | }); 43 | return ret === undefined || 44 | ts.isExportAssignment(ret) || 45 | ts.isExportDeclaration(ret) 46 | ? ret 47 | : visitEachChild(ret, visitor); 48 | }; 49 | return visitor; 50 | }; 51 | 52 | // tslint:disable 53 | const imDeclarations = getMacroImportDeclarations(node).map(x => ({ 54 | bindings: getBindingIdentifiers(x), 55 | transFunc: require(x.moduleSpecifier.getText().slice(1, -1)) 56 | .____$$$$____typescriptMacroNodeTransformFunction____$$$$____ as TypeScriptMacroNodeTransformFunction 57 | /* Magic number 1 and -1 is for remove leading and ending quote */ 58 | })); 59 | const a = imDeclarations.reduce( 60 | (node, curr) => visitEachChild(node, visitorFactory(curr)), 61 | node 62 | ); 63 | return a; 64 | // tslint:enable 65 | }; 66 | return { 67 | before: [beforeTransformerFactory], 68 | after: [], 69 | }; 70 | }; 71 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * type defines for macros 3 | */ 4 | 5 | import * as ts from 'typescript'; 6 | 7 | export interface NodeTransformParameter { 8 | /** 9 | * the node which is visited 10 | */ 11 | readonly node: ts.Node; 12 | /** 13 | * the languageService of whole Transformer 14 | * 15 | * it may not work at ts-loader and awesome-typescript loader for lack of corresponding API support.(need PR for these projects) 16 | * @experimental 17 | */ 18 | readonly languageService: ts.LanguageService; 19 | /** 20 | * function to determine if it is the identifier imported by this import declaration 21 | * 22 | * @param node the Identifier node 23 | * @returns false if it is not variable imported by this macros. string for the referenced bind of this macros. 24 | */ 25 | reference(node: ts.Identifier): false | string; 26 | } 27 | /** 28 | * all macros should export ____$$$$____typescriptMacroNodeTransformFunction____$$$$____ 29 | * 30 | * which is function and whose type sig is TypeScriptMacroNodeTransformFunctions 31 | * 32 | */ 33 | export type TypeScriptMacroNodeTransformFunction = ( 34 | param: NodeTransformParameter, 35 | ) => ts.Node | undefined; 36 | 37 | export type TransformerFactoryCreator = ( 38 | languageService: ts.LanguageService, 39 | ) => ts.CustomTransformers; 40 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/utils/getBindingIdentifiers.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable: missing-jsdoc 2 | 3 | import * as ts from 'typescript'; 4 | import { getUses } from './getUses'; 5 | 6 | export type IdentifiersMap = { readonly [key: string]: ts.Identifier[] }; 7 | 8 | /** 9 | * get binding identifiers of `ImportDeclaration` 10 | * 11 | * @param node typescript source file 12 | * @returns map of Identifiers, whos key is import name and value is referenced identifiers 13 | */ 14 | export function getBindingIdentifiers( 15 | node: ts.ImportDeclaration, 16 | ): IdentifiersMap { 17 | if (node.importClause == null) { 18 | return {}; 19 | } 20 | 21 | // tslint:disable-next-line: readonly-keyword 22 | const ret: { [key: string]: ts.Identifier[] } = {}; 23 | 24 | if (node.importClause.namedBindings !== undefined) { 25 | if (ts.isNamespaceImport(node.importClause.namedBindings)) { 26 | throw new Error('namespace import are not allowd'); 27 | } 28 | if (ts.isNamedImports(node.importClause.namedBindings)) { 29 | node.importClause.namedBindings.elements.forEach( 30 | ({ propertyName, name }) => { 31 | const key = 32 | propertyName === undefined ? name.text : propertyName.text; 33 | ret[key] = getUses(name); 34 | }, 35 | ); 36 | } 37 | } 38 | // default import 39 | if (node.importClause.name !== undefined) { 40 | const then = getUses(node.importClause.name); 41 | ret.default = [...(ret.default === undefined ? [] : ret.default), ...then]; 42 | } 43 | 44 | return ret; 45 | } 46 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/utils/getMacroImportDeclarations.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable: missing-jsdoc 2 | 3 | import * as ts from 'typescript'; 4 | import { isMacroImportDeclaration } from './isMacroImportDeclaration'; 5 | 6 | /** 7 | * get all TypeScriptMacro import declarations 8 | * 9 | * @param node typescript source file 10 | * @returns array of import declarations whose moduleSpecifier ends with `.tsmacro` 11 | * 12 | * ### sample usage 13 | * 14 | * if node is `ts.SourceFile` as below: 15 | * 16 | * ```ts 17 | * import { useAutoMemo } from '@-.-/hooks.tsmacro'; 18 | * import * as React from 'react'; 19 | * import { useAutoMemo as useAutoMemo2 } from '@-.-/hooks.tsmacro'; 20 | * import uppercase from '@-.-/uppercase.tsmacro'; 21 | * import 'foobar.tsmacro'; 22 | * ``` 23 | * 24 | * call this function will return an Array which length is 4 (all import declaration nodes except import react). 25 | */ 26 | export function getMacroImportDeclarations(node: ts.SourceFile) { 27 | const declarations = node.statements.filter(x => 28 | ts.isImportDeclaration(x), 29 | ) as ts.ImportDeclaration[]; 30 | 31 | return declarations.filter(isMacroImportDeclaration); 32 | } 33 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/utils/getUses.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable: missing-jsdoc 2 | 3 | import { collectVariableUsage } from 'tsutils'; 4 | import * as ts from 'typescript'; 5 | 6 | /** 7 | * return the use identifiers's list of declaration 8 | * @param declarationIdentifier identifier of Declaration 9 | * @returns identifier list of the uses of declaration 10 | */ 11 | export function getUses(declarationIdentifier: ts.Identifier) { 12 | // tslint:disable-next-line: no-non-null-assertion 13 | return collectVariableUsage(declarationIdentifier.getSourceFile()) 14 | .get(declarationIdentifier)! 15 | .uses.map(x => x.location); 16 | } 17 | -------------------------------------------------------------------------------- /packages/typescript-macros/src/utils/isMacroImportDeclaration.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable: missing-jsdoc 2 | 3 | import * as ts from 'typescript'; 4 | 5 | /** 6 | * 7 | * @param node ImportDeclaration node 8 | * @returns is it a macro import declaration 9 | */ 10 | export function isMacroImportDeclaration(node: ts.ImportDeclaration) { 11 | return /\.(ts)?macro(\"|\')$/.test(node.moduleSpecifier.getText()); 12 | } 13 | -------------------------------------------------------------------------------- /packages/typescript-macros/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsserver-plugin.tsconfig", 3 | "compilerOptions": { 4 | "strict": true, 5 | "lib": ["esnext"] 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-microsoft-contrib", 4 | "tslint-immutable/all" 5 | ], 6 | "rules": { 7 | "trailing-comma": [true, { 8 | "multiline": "always", 9 | "singleline": "never" 10 | }], 11 | "readonly-array": false, 12 | "no-method-signature": false, 13 | "no-if-statement": false, 14 | "typedef": false, 15 | "no-mixed-interface": false, 16 | "no-expression-statement": false, 17 | "no-object-mutation": false, 18 | "interface-name": false, 19 | "newline-before-return": false, 20 | "prefer-type-cast": false, 21 | "no-angle-bracket-type-assertion": true, 22 | "import-name": false, 23 | "no-relative-imports": false, 24 | 25 | "no-cookies": false, 26 | "await-promise": false, 27 | "match-default-export-name": false, 28 | "no-floating-promises": false, 29 | "no-for-in-array": false, 30 | "no-unsafe-any": false, 31 | "no-use-before-declare": false, 32 | "promise-function-async": false, 33 | "restrict-plus-operands": false, 34 | "strict-boolean-expressions": false, 35 | "completed-docs": false, 36 | "no-unnecessary-qualifier": false, 37 | "no-unnecessary-type-assertion": false, 38 | "no-void-expression": false, 39 | "use-default-type-parameter": false 40 | }, 41 | "linterOptions": { 42 | "exclude": [ 43 | "node_modules/**/*.ts" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tsserver-plugin.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "plugins": [{ "name": "typescript-tslint-plugin" } ] 4 | } 5 | } 6 | --------------------------------------------------------------------------------