diff --git a/src/loaders/js.ts b/src/loaders/js.ts index e7e79fe..5cdf94d 100644 --- a/src/loaders/js.ts +++ b/src/loaders/js.ts @@ -2,6 +2,7 @@ import { transform } from "esbuild"; import jiti from "jiti"; import type { Loader, LoaderResult } from "../loader"; +import { getOutputExtension } from "../utils"; const DECLARATION_RE = /\.d\.[cm]?ts$/; const CM_LETTER_RE = /(?<=\.)(c|m)(?=[jt]s$)/; @@ -55,10 +56,7 @@ export const jsLoader: Loader = async (input, { options }) => { .replace("module.exports = void 0;", ""); } - let extension = isCjs ? ".js" : ".mjs"; // TODO: Default to .cjs in next major version - if (options.ext) { - extension = options.ext.startsWith(".") ? options.ext : `.${options.ext}`; - } + const extension = getOutputExtension(options); output.push({ contents, diff --git a/src/utils/dts.ts b/src/utils/dts.ts index 279cac1..87b8fa6 100644 --- a/src/utils/dts.ts +++ b/src/utils/dts.ts @@ -3,6 +3,7 @@ import { findStaticImports, findExports, findTypeExports } from "mlly"; import { resolve } from "pathe"; import type { TSConfig } from "pkg-types"; import type { MkdistOptions } from "../make"; +import { getOutputExtension } from "./index"; export async function normalizeCompilerOptions( _options: TSConfig["compilerOptions"], @@ -60,7 +61,10 @@ export function extractDeclarations( const dtsFilename = filename.replace(JSX_EXT_RE, ".d.$1ts"); let contents = vfs.get(dtsFilename) || ""; if (opts?.addRelativeDeclarationExtensions) { - const ext = filename.match(JS_EXT_RE)?.[0].replace(/ts$/, "js") || ".js"; + const srcExt = + filename.match(JS_EXT_RE)?.[0].replace(/ts$/, "js") || ".js"; + const ext = getOutputExtension(opts); + const imports = findStaticImports(contents); const exports = findExports(contents); const typeExports = findTypeExports(contents); @@ -69,7 +73,8 @@ export function extractDeclarations( continue; } const srcPath = resolve(filename, "..", spec.specifier); - const srcDtsPath = srcPath + ext.replace(JS_EXT_RE, ".d.$1ts"); + const srcDtsPath = srcPath + srcExt.replace(JS_EXT_RE, ".d.$1ts"); + let specifier = spec.specifier; try { if (!vfs.get(srcDtsPath)) { diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..0c6f220 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,10 @@ +import type { MkdistOptions } from "../make"; + +export function getOutputExtension(options: MkdistOptions) { + const isCjs = options.format === "cjs"; + let ext = isCjs ? ".js" : ".mjs"; // TODO: Default to .cjs in next major version + if (options.ext) { + ext = options.ext.startsWith(".") ? options.ext : `.${options.ext}`; + } + return ext; +} diff --git a/test/index.test.ts b/test/index.test.ts index 854fc2e..29e98db 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -156,6 +156,168 @@ describe("mkdist", () => { "manual declaration", ); + expect(await readFile(resolve(rootDir, "dist/star/index.d.ts"), "utf8")) + .toMatchInlineSnapshot(` + "export * from "./other.mjs"; + export type { Other } from "./other.mjs"; + " + `); + + expect(await readFile(resolve(rootDir, "dist/dir-export.d.ts"), "utf8")) + .toMatchInlineSnapshot(` + "export { default as bar } from "./bar.mjs"; + export * from "./star/index.mjs"; + " + `); + + expect( + await readFile(resolve(rootDir, "dist/bar/esm.d.mts"), "utf8"), + ).toMatch("declare"); + + expect( + await readFile(resolve(rootDir, "dist/components/index.d.ts"), "utf8"), + ).toMatchInlineSnapshot(` + "export * as jsx from "./jsx.jsx.mjs"; + export * as tsx from "./tsx.tsx.mjs"; + export * as blank from "./blank.vue.mjs"; + export * as scriptSetupTS from "./script-setup-ts.vue.mjs"; + export * as scriptMultiBlock from "./script-multi-block.vue.mjs"; + export * as ts from "./ts.vue.mjs"; + " + `); + + expect( + await readFile(resolve(rootDir, "dist/components/ts.vue.d.ts"), "utf8"), + ).toMatchInlineSnapshot(` + "declare const _default: import("vue").DefineComponent<{}, {}, { + test: string; + str: "test"; + }, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + export default _default; + " + `); + + expect( + await readFile( + resolve(rootDir, "dist/components/blank.vue.d.ts"), + "utf8", + ), + ).toMatchInlineSnapshot(` + "declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + export default _default; + " + `); + + expect( + await readFile( + resolve(rootDir, "dist/components/script-multi-block.vue.d.ts"), + "utf8", + ), + ).toMatchInlineSnapshot(` + "interface MyComponentProps { + msg: string; + } + declare const _default: import("vue").DefineComponent>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + export default _default; + type __VLS_NonUndefinedable = T extends undefined ? never : T; + type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick ? { + type: import('vue').PropType<__VLS_NonUndefinedable>; + } : { + type: import('vue').PropType; + required: true; + }; + }; + " + `); + + expect( + await readFile( + resolve(rootDir, "dist/components/script-setup-ts.vue.d.ts"), + "utf8", + ), + ).toMatchInlineSnapshot(` + "import { Color } from "#prop-types"; + type __VLS_Props = { + msg: string; + color: Color; + }; + declare const _default: import("vue").DefineComponent>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + export default _default; + type __VLS_NonUndefinedable = T extends undefined ? never : T; + type __VLS_TypePropsToOption = { + [K in keyof T]-?: {} extends Pick ? { + type: import('vue').PropType<__VLS_NonUndefinedable>; + } : { + type: import('vue').PropType; + required: true; + }; + }; + " + `); + }, 50_000); + + it("mkdist (emit types) [js]", async () => { + const rootDir = resolve(__dirname, "fixture"); + const { writtenFiles } = await mkdist({ + rootDir, + declaration: true, + addRelativeDeclarationExtensions: true, + ext: "js", + }); + expect(writtenFiles.sort()).toEqual( + [ + "dist/README.md", + "dist/bar.d.ts", + "dist/bar.js", + "dist/demo.css", + "dist/dir-export.d.ts", + "dist/dir-export.js", + "dist/foo.js", + "dist/foo.d.ts", + "dist/index.js", + "dist/index.d.ts", + "dist/star/index.js", + "dist/star/index.d.ts", + "dist/star/other.js", + "dist/star/other.d.ts", + "dist/types.d.ts", + "dist/components/index.js", + "dist/components/index.d.ts", + "dist/components/blank.vue", + "dist/components/blank.vue.d.ts", + "dist/components/js.vue", + "dist/components/js.vue.d.ts", + "dist/components/script-multi-block.vue", + "dist/components/script-multi-block.vue.d.ts", + "dist/components/script-setup-ts.vue", + "dist/components/script-setup-ts.vue.d.ts", + "dist/components/ts.vue", + "dist/components/ts.vue.d.ts", + "dist/components/jsx.js", + "dist/components/tsx.js", + "dist/components/jsx.d.ts", + "dist/components/tsx.d.ts", + "dist/bar/index.js", + "dist/bar/index.d.ts", + "dist/bar/esm.js", + "dist/bar/esm.d.mts", + "dist/ts/test1.js", + "dist/ts/test2.js", + "dist/ts/test1.d.mts", + "dist/ts/test2.d.cts", + "dist/nested.css", + "dist/prop-types/index.js", + "dist/prop-types/index.d.ts", + ] + .map((f) => resolve(rootDir, f)) + .sort(), + ); + + expect(await readFile(resolve(rootDir, "dist/foo.d.ts"), "utf8")).toMatch( + "manual declaration", + ); + expect(await readFile(resolve(rootDir, "dist/star/index.d.ts"), "utf8")) .toMatchInlineSnapshot(` "export * from "./other.js"; @@ -589,8 +751,8 @@ describe("mkdist with vue-tsc v1", () => { expect(await readFile(resolve(rootDir, "dist/star/index.d.ts"), "utf8")) .toMatchInlineSnapshot(` - "export * from "./other.js"; - export type { Other } from "./other.js"; + "export * from "./other.mjs"; + export type { Other } from "./other.mjs"; " `); expect( @@ -600,12 +762,12 @@ describe("mkdist with vue-tsc v1", () => { expect( await readFile(resolve(rootDir, "dist/components/index.d.ts"), "utf8"), ).toMatchInlineSnapshot(` - "export * as jsx from "./jsx.jsx.js"; - export * as tsx from "./tsx.tsx.js"; - export * as blank from "./blank.vue.js"; - export * as scriptSetupTS from "./script-setup-ts.vue.js"; - export * as scriptMultiBlock from "./script-multi-block.vue.js"; - export * as ts from "./ts.vue.js"; + "export * as jsx from "./jsx.jsx.mjs"; + export * as tsx from "./tsx.tsx.mjs"; + export * as blank from "./blank.vue.mjs"; + export * as scriptSetupTS from "./script-setup-ts.vue.mjs"; + export * as scriptMultiBlock from "./script-multi-block.vue.mjs"; + export * as ts from "./ts.vue.mjs"; " `); @@ -846,8 +1008,8 @@ describe("mkdist with vue-tsc ~v2.0.21", () => { expect(await readFile(resolve(rootDir, "dist/star/index.d.ts"), "utf8")) .toMatchInlineSnapshot(` - "export * from "./other.js"; - export type { Other } from "./other.js"; + "export * from "./other.mjs"; + export type { Other } from "./other.mjs"; " `); expect( @@ -857,12 +1019,12 @@ describe("mkdist with vue-tsc ~v2.0.21", () => { expect( await readFile(resolve(rootDir, "dist/components/index.d.ts"), "utf8"), ).toMatchInlineSnapshot(` - "export * as jsx from "./jsx.jsx.js"; - export * as tsx from "./tsx.tsx.js"; - export * as blank from "./blank.vue.js"; - export * as scriptSetupTS from "./script-setup-ts.vue.js"; - export * as scriptMultiBlock from "./script-multi-block.vue.js"; - export * as ts from "./ts.vue.js"; + "export * as jsx from "./jsx.jsx.mjs"; + export * as tsx from "./tsx.tsx.mjs"; + export * as blank from "./blank.vue.mjs"; + export * as scriptSetupTS from "./script-setup-ts.vue.mjs"; + export * as scriptMultiBlock from "./script-multi-block.vue.mjs"; + export * as ts from "./ts.vue.mjs"; " `);