Test complete
This commit is contained in:
parent
6f28290529
commit
21003af738
45
a.js
Executable file
45
a.js
Executable file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs")
|
||||
const babel = require("@babel/core")
|
||||
|
||||
const code = `
|
||||
export class A {
|
||||
#a: string;
|
||||
}
|
||||
`
|
||||
|
||||
const res = babel.transform(code, {
|
||||
filename: "a.ts",
|
||||
presets: [
|
||||
["@babel/preset-env", { targets: { node: 14 } }],
|
||||
["@babel/preset-typescript"],
|
||||
],
|
||||
plugins: [
|
||||
({ types: t }) => ({
|
||||
visitor: {
|
||||
ClassDeclaration(path, state) {
|
||||
state.a ??= new WeakSet()
|
||||
if (state.a.has(path.node)) {
|
||||
return
|
||||
}
|
||||
state.a.add(path.node)
|
||||
|
||||
path.parentPath.replaceWithMultiple([
|
||||
t.exportNamedDeclaration(
|
||||
t.variableDeclaration("let", [
|
||||
t.variableDeclarator(path.node.id, {
|
||||
...path.node,
|
||||
type: "ClassExpression",
|
||||
}),
|
||||
]),
|
||||
[],
|
||||
null
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
console.log(res.code)
|
@ -6,11 +6,12 @@
|
||||
"scripts": {
|
||||
"test": "mocha test/**/*.ts",
|
||||
"build": "./scripts/build.js",
|
||||
"postinstall": "mkdir -p lib; cd test/snapshots && ls | sed \"s/.*/'&'/\" | paste -sd '|' | sed -e \"s/^/export type Snapshot = /\" - > ../snapshots.d.ts"
|
||||
"postinstall": "mkdir -p lib; ./scripts/codegen.js test/snapshots > test/snapshots.d.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.8",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
|
||||
"@babel/preset-env": "^7.14.8",
|
||||
"@babel/preset-typescript": "^7.14.5",
|
||||
"@babel/register": "^7.14.5",
|
||||
"@babel/types": "^7.14.8",
|
||||
|
993
pnpm-lock.yaml
generated
993
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
5
scripts/codegen.js
Executable file
5
scripts/codegen.js
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs")
|
||||
const [arg] = process.argv.slice(2)
|
||||
const files = fs.readdirSync(arg).filter(name => name[0] !== ".")
|
||||
console.log("export type Snapshot = " + files.map(v => JSON.stringify(v)).join(" | "))
|
50
src/index.ts
50
src/index.ts
@ -50,9 +50,10 @@ export default ({ types: t }: typeof babel): babel.PluginObj<State> => ({
|
||||
if (state.visited.has(path.node)) return
|
||||
state.visited.add(path.node)
|
||||
|
||||
const modifiers: (t.Expression | t.Statement)[] = []
|
||||
const suffix: (t.Expression | t.Statement)[] = []
|
||||
const classDecorators: t.Expression[] = []
|
||||
const prefixes: t.Statement[] = []
|
||||
let replace = true
|
||||
|
||||
const classID = path.node.id
|
||||
|
||||
@ -66,9 +67,14 @@ export default ({ types: t }: typeof babel): babel.PluginObj<State> => ({
|
||||
|
||||
for (const child of path.node.body.body) {
|
||||
switch (child.type) {
|
||||
case "ClassPrivateMethod":
|
||||
case "ClassPrivateProperty":
|
||||
replace = false
|
||||
break
|
||||
|
||||
case "ClassProperty":
|
||||
if (!child.decorators) break
|
||||
modifiers.push(
|
||||
suffix.push(
|
||||
t.callExpression(state.__decorate(), [
|
||||
t.arrayExpression(child.decorators.map(d => d.expression)),
|
||||
child.static
|
||||
@ -119,7 +125,7 @@ export default ({ types: t }: typeof babel): babel.PluginObj<State> => ({
|
||||
|
||||
if (!list.length) break
|
||||
|
||||
modifiers.push(
|
||||
suffix.push(
|
||||
t.callExpression(state.__decorate(), [
|
||||
t.arrayExpression(list),
|
||||
t.memberExpression(classID, t.identifier("prototype")),
|
||||
@ -133,16 +139,16 @@ export default ({ types: t }: typeof babel): babel.PluginObj<State> => ({
|
||||
}
|
||||
}
|
||||
|
||||
if (!classDecorators.length && !modifiers.length && !prefixes.length) return
|
||||
if (!classDecorators.length && !suffix.length && !prefixes.length) return
|
||||
let replaceWith: t.Statement | undefined
|
||||
|
||||
const replacees: t.Statement[] = prefixes.slice()
|
||||
if (classDecorators.length) {
|
||||
replacees.push(
|
||||
t.variableDeclaration("let", [
|
||||
if (replace) {
|
||||
replaceWith = t.variableDeclaration("let", [
|
||||
t.variableDeclarator(classID, { ...path.node, type: "ClassExpression" }),
|
||||
])
|
||||
)
|
||||
modifiers.push(
|
||||
}
|
||||
suffix.push(
|
||||
t.assignmentExpression(
|
||||
"=",
|
||||
classID,
|
||||
@ -154,27 +160,35 @@ export default ({ types: t }: typeof babel): babel.PluginObj<State> => ({
|
||||
)
|
||||
|
||||
if (isNamedExport) {
|
||||
modifiers.push(
|
||||
suffix.push(
|
||||
t.exportNamedDeclaration(undefined, [t.exportSpecifier(classID, classID)])
|
||||
)
|
||||
} else if (isDefaultExport) {
|
||||
modifiers.push(t.exportDefaultDeclaration(classID))
|
||||
suffix.push(t.exportDefaultDeclaration(classID))
|
||||
}
|
||||
} else {
|
||||
} else if (replace) {
|
||||
let node: t.Statement = path.node
|
||||
if (isNamedExport) {
|
||||
node = t.exportNamedDeclaration(node, [], null)
|
||||
} else if (isDefaultExport) {
|
||||
node = t.exportDefaultDeclaration(node)
|
||||
}
|
||||
replacees.push(node)
|
||||
replaceWith = node
|
||||
}
|
||||
|
||||
replacees.push(
|
||||
...modifiers.map(e => (t.isStatement(e) ? e : t.expressionStatement(e)))
|
||||
)
|
||||
const replacementTarget = isNamedExport || isDefaultExport ? path.parentPath : path
|
||||
replacementTarget.replaceWithMultiple(replacees)
|
||||
const pathInContext = isNamedExport || isDefaultExport ? path.parentPath : path
|
||||
if (prefixes.length) {
|
||||
pathInContext.insertBefore(prefixes)
|
||||
}
|
||||
if (suffix.length) {
|
||||
const nodes = suffix.map(s => (t.isStatement(s) ? s : t.expressionStatement(s)))
|
||||
if (replace) {
|
||||
const [newPath] = pathInContext.replaceWith(replaceWith!)
|
||||
newPath.insertAfter(nodes)
|
||||
} else {
|
||||
pathInContext.insertAfter(nodes)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { readFileSync } from "fs"
|
||||
import { readFileSync, existsSync } from "fs"
|
||||
import { describe, it } from "mocha"
|
||||
import { resolve } from "path"
|
||||
import { expect } from "chai"
|
||||
@ -14,13 +14,20 @@ const equal = (name: Snapshot) => {
|
||||
const folder = resolve(__dirname, "snapshots", name)
|
||||
const actual = readFileSync(resolve(folder, "input.txt"), "utf-8")
|
||||
const expected = readFileSync(resolve(folder, "output.txt"), "utf-8")
|
||||
const babelrc: babel.TransformOptions = existsSync(resolve(folder, ".babelrc"))
|
||||
? JSON.parse(readFileSync(resolve(folder, ".babelrc"), "utf-8"))
|
||||
: {}
|
||||
|
||||
const transformed = transform(actual, {
|
||||
parserOpts: {
|
||||
...babelrc.parserOpts,
|
||||
plugins: ["decorators-legacy", "typescript"],
|
||||
},
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
presets: babelrc.presets,
|
||||
plugins: [
|
||||
...(babelrc.plugins ?? []),
|
||||
plugin,
|
||||
{
|
||||
visitor: {
|
||||
@ -52,11 +59,15 @@ describe("tsc-decorator", () => {
|
||||
equal("computedProperties")
|
||||
})
|
||||
|
||||
it("works with class/consrtuctor decorators", () => {
|
||||
it("works with class/constructor decorators", () => {
|
||||
equal("constructor")
|
||||
})
|
||||
|
||||
it("does not interfere with export declarations", () => {
|
||||
equal("exports")
|
||||
})
|
||||
|
||||
it("works with private properties", () => {
|
||||
equal("privateProperties")
|
||||
})
|
||||
})
|
||||
|
3
test/snapshots/privateProperties/.babelrc
Normal file
3
test/snapshots/privateProperties/.babelrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": [["@babel/preset-env", { "targets": { "node": 14 } }]]
|
||||
}
|
4
test/snapshots/privateProperties/input.txt
Normal file
4
test/snapshots/privateProperties/input.txt
Normal file
@ -0,0 +1,4 @@
|
||||
@Service()
|
||||
export class B {
|
||||
#remote: Service
|
||||
}
|
22
test/snapshots/privateProperties/output.txt
Normal file
22
test/snapshots/privateProperties/output.txt
Normal file
@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true,
|
||||
});
|
||||
|
||||
exports.B = exports.B = void 0;
|
||||
|
||||
var _tslib = require("tslib");
|
||||
var _remote = /*#__PURE__*/ new WeakMap();
|
||||
|
||||
exports.B = exports.B = B = (0, _tslib.__decorate)([Service()], B);
|
||||
|
||||
class B {
|
||||
constructor() {
|
||||
_remote.set(this, {
|
||||
writable: true,
|
||||
value: void 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exports.B = exports.B = B;
|
Loading…
x
Reference in New Issue
Block a user