diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fff7b66..3489624 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,6 @@ settings: excludeLinksFromLockfile: false overrides: - source-map: npm:source-map-js@latest array-includes: npm:@nolyfill/array-includes@latest array.prototype.flat: npm:@nolyfill/array.prototype.flat@latest array.prototype.flatmap: npm:@nolyfill/array.prototype.flatmap@latest @@ -1047,16 +1046,16 @@ packages: '@nolyfill/shared': 1.0.28 dev: true - /@nolyfill/available-typed-arrays@1.0.24: - resolution: {integrity: sha512-6PZEfv0F4sepusXYG3nqTntnKSPDCetmkrxnVl808hSdEdI2NzbZWK6Bqo28/cVqCB9sCVBz2C8TQa/sjLJt7A==} + /@nolyfill/available-typed-arrays@1.0.29: + resolution: {integrity: sha512-cOQEAPTlnmNDaWtGJB2RTR3O/Yw09b2vNky+TVYplvCMWBa9IZ8Ai8OBtWhXevAlG0EF46RAzv6YrDNbd1Fl2w==} engines: {node: '>=12.4.0'} dev: true - /@nolyfill/define-properties@1.0.24: - resolution: {integrity: sha512-8XzX+oOf8ra+bS/yDqcok1l7p/i+/N9JYpO3tgI7+wOBnHAL0AB/mxT+qyoIdYnSBBwXSoaMZWJqEnxMe31BHg==} + /@nolyfill/define-properties@1.0.29: + resolution: {integrity: sha512-oowfUnNgrcAFRVF4aynDsaWn0OJxh9TUwmf8Aae9Q59XBp48GAY6as1A1oF9xdYdFeub9ezF+9DV+DojLkLVXQ==} engines: {node: '>=12.4.0'} dependencies: - '@nolyfill/shared': 1.0.24 + '@nolyfill/shared': 1.0.29 dev: true /@nolyfill/function-bind@1.0.21: @@ -1064,8 +1063,8 @@ packages: engines: {node: '>=12.4.0'} dev: true - /@nolyfill/gopd@1.0.24: - resolution: {integrity: sha512-//EzMxdolcfpMEpF6vZxzx3XZcOScEcdb7dG24R7POD0fvG+WmugQwcqwDbS/wFQ3Kv9NIXka8zbSKLFENyFoA==} + /@nolyfill/gopd@1.0.29: + resolution: {integrity: sha512-B7ijoYZ7YGNpe+lv3zrJWTrz5yzwz0XONKr8RrzTMyjBDP+FZpgNqzjqh1fQ/BREL1alKdwHPMcucWTOD0+4YA==} engines: {node: '>=12.4.0'} dev: true @@ -1079,13 +1078,13 @@ packages: engines: {node: '>=12.4.0'} dev: true - /@nolyfill/is-arguments@1.0.28: - resolution: {integrity: sha512-WPc5PTAgcTDw0QQ/j+oo2xU6VzLVJ4kbcxrIk4vT0T83TYKVZRfbFT6Vf9auCLg5HcgBJBlEC7x5w+J+GxHnBQ==} + /@nolyfill/is-arguments@1.0.29: + resolution: {integrity: sha512-NDs3NazdLsJCkLP0xmztFE92+dHcU8EeFaifHwid+NtXfi/lJt37Y0PgFlUoaJlC/Cdle1t/CiwPPDQ9LqHEDw==} engines: {node: '>=12.4.0'} dev: true - /@nolyfill/is-generator-function@1.0.28: - resolution: {integrity: sha512-Lmb7ihogbV5G5S5FRQTvyiQWpPZmZp9UB4rW5J28pMv41eBFFK0PWfY1DpfHdzRzLS6mVuh9RECPyjVrrXiX5g==} + /@nolyfill/is-generator-function@1.0.29: + resolution: {integrity: sha512-dBNj5iWhFQUM3JG8XxdwYMQMes44GY9cHCeBXAZNrsYAuQofcL0ke4c9BNh/2FPSVr+sVjErbQHLpaos/XMpug==} engines: {node: '>=12.4.0'} dev: true @@ -1103,19 +1102,19 @@ packages: '@nolyfill/shared': 1.0.28 dev: true - /@nolyfill/shared@1.0.24: - resolution: {integrity: sha512-TGCpg3k5N7jj9AgU/1xFw9K1g4AC1vEE5ZFkW77oPNNLzprxT17PvFaNr/lr3BkkT5fJ5LNMntaTIq+pyWaeEA==} - dev: true - /@nolyfill/shared@1.0.28: resolution: {integrity: sha512-UJTshFMDgugBcYXGLopbL1enYpGREOEfjUMQKLPLeJqWfbfElGtYbGbUcucCENa7cicGo3M5u/DnPiZe/PYQyw==} dev: true - /@nolyfill/which-typed-array@1.0.24: - resolution: {integrity: sha512-DyeHLuWXQ+q4TYFhSnl6eZ0qadrNk9SJPL13pdoIIaz8TlKKIbv/9UF6wulmlFohJGQizoT5vew5mW1rK1Dlbw==} + /@nolyfill/shared@1.0.29: + resolution: {integrity: sha512-Z8TABKv9kk8LrOtrfShpjeg9BhPI3AKZ78eVsVduSEhti8D59Fk+UmgO7iRJHLd1aMB7A1aIb+bQtcCjVmWJoA==} + dev: true + + /@nolyfill/which-typed-array@1.0.29: + resolution: {integrity: sha512-FqPYrNSzpICKHoKRVUjNfCS0L5B7uK/gGoE32gbV2THPfpwbLfM1e8C9NuOXhNtMjP3G9ni0DXUUipSCaWdfLQ==} engines: {node: '>=12.4.0'} dependencies: - '@nolyfill/shared': 1.0.24 + '@nolyfill/shared': 1.0.29 dev: true /@polka/url@1.0.0-next.24: @@ -1665,7 +1664,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 es-shim-unscopables: 1.0.2 get-intrinsic: 1.2.2 @@ -1677,7 +1676,7 @@ packages: dependencies: array-buffer-byte-length: 1.0.0 call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 get-intrinsic: 1.2.2 is-array-buffer: 3.0.2 @@ -2059,7 +2058,7 @@ packages: engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.2 - gopd: /@nolyfill/gopd@1.0.24 + gopd: /@nolyfill/gopd@1.0.29 has-property-descriptors: 1.0.1 dev: true @@ -2135,7 +2134,7 @@ packages: dependencies: array-buffer-byte-length: 1.0.0 arraybuffer.prototype.slice: 1.0.2 - available-typed-arrays: /@nolyfill/available-typed-arrays@1.0.24 + available-typed-arrays: /@nolyfill/available-typed-arrays@1.0.29 call-bind: 1.0.5 es-set-tostringtag: 2.0.2 es-to-primitive: 1.2.1 @@ -2143,7 +2142,7 @@ packages: get-intrinsic: 1.2.2 get-symbol-description: 1.0.0 globalthis: 1.0.3 - gopd: /@nolyfill/gopd@1.0.24 + gopd: /@nolyfill/gopd@1.0.29 has-property-descriptors: 1.0.1 has-proto: 1.0.1 has-symbols: /@nolyfill/has-symbols@1.0.21 @@ -2171,7 +2170,7 @@ packages: typed-array-byte-offset: 1.0.0 typed-array-length: 1.0.4 unbox-primitive: 1.0.2 - which-typed-array: /@nolyfill/which-typed-array@1.0.24 + which-typed-array: /@nolyfill/which-typed-array@1.0.29 dev: true /es-set-tostringtag@2.0.2: @@ -2850,7 +2849,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 functions-have-names: 1.2.3 dev: true @@ -2972,7 +2971,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 dev: true /globby@11.1.0: @@ -3220,7 +3219,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 dev: true /is-negative-zero@2.0.2: @@ -3293,7 +3292,7 @@ packages: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: /@nolyfill/which-typed-array@1.0.24 + which-typed-array: /@nolyfill/which-typed-array@1.0.29 dev: true /is-weakref@1.0.2: @@ -3337,7 +3336,7 @@ packages: dependencies: debug: 4.3.4 istanbul-lib-coverage: 3.2.2 - source-map: /source-map-js@1.0.2 + source-map: 0.6.1 transitivePeerDependencies: - supports-color dev: true @@ -3766,7 +3765,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 has-symbols: /@nolyfill/has-symbols@1.0.21 object-keys: 1.1.1 dev: true @@ -3776,7 +3775,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 dev: true @@ -3784,7 +3783,7 @@ packages: resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 get-intrinsic: 1.2.2 dev: true @@ -4057,7 +4056,7 @@ packages: assert: 2.1.0 ast-types: 0.16.1 esprima: 4.0.1 - source-map: /source-map-js@1.0.2 + source-map: 0.6.1 tslib: 2.6.2 dev: true @@ -4071,7 +4070,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 set-function-name: 2.0.1 dev: true @@ -4239,7 +4238,7 @@ packages: dependencies: define-data-property: 1.1.1 get-intrinsic: 1.2.2 - gopd: /@nolyfill/gopd@1.0.24 + gopd: /@nolyfill/gopd@1.0.29 has-property-descriptors: 1.0.1 dev: true @@ -4330,6 +4329,11 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -4383,7 +4387,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 dev: true @@ -4391,7 +4395,7 @@ packages: resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 dev: true @@ -4399,7 +4403,7 @@ packages: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: call-bind: 1.0.5 - define-properties: /@nolyfill/define-properties@1.0.24 + define-properties: /@nolyfill/define-properties@1.0.29 es-abstract: 1.22.3 dev: true @@ -4628,7 +4632,7 @@ packages: resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} engines: {node: '>= 0.4'} dependencies: - available-typed-arrays: /@nolyfill/available-typed-arrays@1.0.24 + available-typed-arrays: /@nolyfill/available-typed-arrays@1.0.29 call-bind: 1.0.5 for-each: 0.3.3 has-proto: 1.0.1 @@ -4755,10 +4759,10 @@ packages: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} dependencies: inherits: 2.0.4 - is-arguments: /@nolyfill/is-arguments@1.0.28 - is-generator-function: /@nolyfill/is-generator-function@1.0.28 + is-arguments: /@nolyfill/is-arguments@1.0.29 + is-generator-function: /@nolyfill/is-generator-function@1.0.29 is-typed-array: 1.1.12 - which-typed-array: /@nolyfill/which-typed-array@1.0.24 + which-typed-array: /@nolyfill/which-typed-array@1.0.29 dev: true /v8-to-istanbul@9.2.0: diff --git a/src/code.ts b/src/code.ts index 76ed658..06446f9 100644 --- a/src/code.ts +++ b/src/code.ts @@ -20,6 +20,7 @@ export function parseModule( parser: options?.parser || getBabelParser(), ...options, }); + return proxifyModule(node, code); } diff --git a/src/proxy/exports.ts b/src/proxy/exports.ts index a6000b0..8302fc4 100644 --- a/src/proxy/exports.ts +++ b/src/proxy/exports.ts @@ -1,8 +1,16 @@ import * as recast from "recast"; -import type { Program } from "@babel/types"; +import { + Program, + CommentBlock, + CommentLine, + ExportDefaultDeclaration, + ExportNamedDeclaration, +} from "@babel/types"; import type { ProxifiedModule } from "./types"; import { createProxy, literalToAst } from "./_utils"; import { proxify } from "./proxify"; +import { getPropName } from "./object"; +import { ASTNode } from "magicast"; const b = recast.types.builders; @@ -58,10 +66,43 @@ export function createExportsProxy(root: Program, mod: ProxifiedModule) { ); }; + const proxifyComment = new Proxy( + {}, + { + get(_, key) { + const type = + key === "default" + ? "ExportDefaultDeclaration" + : "ExportNamedDeclaration"; + + const node = root.body.find((n) => n.type === type); + console.info(node); + return node?.leadingComments + ?.map((comment) => comment.value) + .join("\n"); + }, + set(_, key, value) { + const type = + key === "default" + ? "ExportDefaultDeclaration" + : "ExportNamedDeclaration"; + + const node = root.body.find((n) => n.type === type) as + | ExportDefaultDeclaration + | ExportNamedDeclaration; + + // @ts-expect-error + node.comments = [b.commentBlock(value, true)]; + return true; + }, + }, + ); + return createProxy( root, { $type: "exports", + $comment: proxifyComment, }, { get(_, prop) { diff --git a/src/proxy/object.ts b/src/proxy/object.ts index cfe911b..f1dc079 100644 --- a/src/proxy/object.ts +++ b/src/proxy/object.ts @@ -1,4 +1,5 @@ import * as recast from "recast"; +import type { CommentBlock, CommentLine, ObjectExpression } from "@babel/types"; import type { ASTNode } from "../types"; import { MagicastError } from "../error"; import type { ProxifiedModule, ProxifiedObject } from "./types"; @@ -7,6 +8,30 @@ import { proxify } from "./proxify"; const b = recast.types.builders; +export const getPropName = ( + prop: ObjectExpression["properties"][0], + mod?: ProxifiedModule, + throwError = false, +) => { + if ("key" in prop && "name" in prop.key) { + return prop.key.name; + } + if ( + prop.type === "ObjectProperty" && + (prop.key.type === "StringLiteral" || + prop.key.type === "NumericLiteral" || + prop.key.type === "BooleanLiteral") + ) { + return prop.key.value.toString(); + } + if (throwError) { + throw new MagicastError(`Casting "${prop.type}" is not supported`, { + ast: prop, + code: mod?.$code, + }); + } +}; + export function proxifyObject( node: ASTNode, mod?: ProxifiedModule, @@ -33,29 +58,6 @@ export function proxifyObject( } }; - const getPropName = ( - prop: (typeof node.properties)[0], - throwError = false, - ) => { - if ("key" in prop && "name" in prop.key) { - return prop.key.name; - } - if ( - prop.type === "ObjectProperty" && - (prop.key.type === "StringLiteral" || - prop.key.type === "NumericLiteral" || - prop.key.type === "BooleanLiteral") - ) { - return prop.key.value.toString(); - } - if (throwError) { - throw new MagicastError(`Casting "${prop.type}" is not supported`, { - ast: prop, - code: mod?.$code, - }); - } - }; - const replaceOrAddProp = (key: string, value: ASTNode) => { const prop = (node.properties as any[]).find( (prop: any) => getPropName(prop) === key, @@ -80,15 +82,63 @@ export function proxifyObject( } }; + const createCommentProxy = ( + node: ASTNode, + ext = {}, + handler = {}, + mod?: ProxifiedModule, + ): any => { + switch (node.type) { + case "ObjectExpression": { + return createProxy(node, ext, { + ...handler, + set(_, key, value) { + const prop = (node.properties as any[]).find( + (p: any) => getPropName(p) === key, + ); + prop.comments = [b.commentBlock(value, true, false)]; + return true; + }, + get(_, key) { + const prop = (node.properties as any[]).find( + (p: any) => getPropName(p) === key, + ); + if (!prop) { + return; + } + + if ( + [ + "ObjectExpression", + "ObjectPattern", + "ObjectTypeAnnotation", + "RecordExpression", + ].includes(prop.value.type) + ) { + return proxify(prop.value, mod); + // return prop + } + return prop.comments + ?.map((comment: CommentBlock | CommentLine) => comment.value) + .join("\n"); + }, + }); + } + } + }; + + const proxifyComment = createCommentProxy(node, {}, {}, mod); + return createProxy( node, { $type: "object", + $comment: proxifyComment, toJSON() { - // @ts-expect-error // eslint-disable-next-line unicorn/no-array-reduce return node.properties.reduce((acc, prop) => { if ("key" in prop && "name" in prop.key) { + // @ts-expect-error acc[prop.key.name] = proxify(prop.value, mod); } return acc; diff --git a/src/proxy/types.ts b/src/proxy/types.ts index 41c3fb7..66ee715 100644 --- a/src/proxy/types.ts +++ b/src/proxy/types.ts @@ -41,8 +41,11 @@ export type ProxifiedObject = { [K in keyof T]: Proxified; } & ProxyBase & { $type: "object"; + $comment: ProxifiedComment; }; +export type ProxifiedComment = any; + export type ProxifiedIdentifier = ProxyBase & { $type: "identifier"; $name: string; diff --git a/test/comment.test.ts b/test/comment.test.ts new file mode 100644 index 0000000..d6695ed --- /dev/null +++ b/test/comment.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from "vitest"; +import { parseModule, generateCode } from "../src"; + +describe("should have $comment", () => { + it("should have $comment proxy", async () => { + const mod = await parseModule(` + export default { + a: 'A', // a commmnet + b: 1, // b comment + // c comment + c: true, + d: { + e: 'E', + // i comment + i: 'I' + } + }; + `); + + // mod.exports.default.a.$comment = 'asdsad' // throw Error because mod.exports.default.a is proxied to a string + + expect(mod.exports.default.$comment.a).eq(" a commmnet"); + mod.exports.default.$comment.a = " THIS IS A "; + + // How to set comment of d ? + mod.exports.default.$comment.d = "THIS IS D"; + + mod.exports.default.$comment.d.$comment.e = "THIS IS E"; + expect(mod.exports.default.$comment.d.$comment.i).toBe(" i comment"); + + console.info(generateCode(mod).code); + expect(generateCode(mod).code).toMatchInlineSnapshot( + `"export default { + /* THIS IS A */ + a: 'A', + b: 1, // b comment + // c comment + c: true, + /*THIS IS D*/ + d: { + /*THIS IS E*/ + e: 'E', + // i comment + i: 'I' + } +};"`, + ); + }); +}); diff --git a/test/general.test.ts b/test/general.test.ts index 146f5ae..a4b47c1 100644 --- a/test/general.test.ts +++ b/test/general.test.ts @@ -1,6 +1,6 @@ import { expect, it, describe } from "vitest"; -import { generateCode, parseModule, parseExpression } from "magicast"; import { generate } from "./_utils"; +import { generateCode, parseModule, parseExpression } from "magicast"; describe("general", () => { it("basic object and array", async () => {