Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(esm): support new import attributes syntax #89

Merged
merged 3 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 48 additions & 30 deletions src/esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,50 @@ import { CodegenOptions } from "./types";
import { genString } from "./string";

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
// https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#sec-imports
export type ESMImport = string | { name: string; as?: string };

export interface ESMCodeGenOptions extends CodegenOptions {
// https://github.com/tc39/proposal-import-attributes
// https://nodejs.org/api/esm.html#import-attributes
attributes?: { type: string };
/** @deprecated use attributes */
assert?: { type: string };
}

export interface DynamicImportOptions extends ESMCodeGenOptions {
comment?: string;
wrapper?: boolean;
interopDefault?: boolean;
}

export function genImport(
specifier: string,
imports?: ESMImport | ESMImport[],
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) {
return _genStatement("import", specifier, imports, options);
}

export function genTypeImport(
specifier: string,
imports: ESMImport[],
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) {
return _genStatement("import type", specifier, imports, options);
}

export function genTypeExport(
specifier: string,
imports: ESMImport[],
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) {
return _genStatement("export type", specifier, imports, options);
}

export const genInlineTypeImport = (
specifier: string,
name = "default",
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) => {
return `typeof ${genDynamicImport(specifier, {
...options,
Expand All @@ -47,7 +60,7 @@ export type ESMExport = string | { name: string; as?: string };
export function genExport(
specifier: string,
exports?: ESMExport | ESMExport[],
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) {
return _genStatement("export", specifier, exports, options);
}
Expand All @@ -58,7 +71,7 @@ function _genStatement(
type: ImportExportType,
specifier: string,
names?: ESMImportOrExport | ESMImportOrExport[],
options: CodegenOptions = {},
options: ESMCodeGenOptions = {},
) {
const specifierString = genString(specifier, options);
if (!names) {
Expand Down Expand Up @@ -86,36 +99,35 @@ function _genStatement(
return `${type} { ${namesString} } from ${genString(
specifier,
options,
)}${_genAssertClause(type, options.assert)};`;
)}${_genImportAttributes(type, options)};`;
}
return `${type} ${namesString} from ${genString(
specifier,
options,
)}${_genAssertClause(type, options.assert)};`;
)}${_genImportAttributes(type, options)};`;
}

function _genAssertClause(type: ImportExportType, assert?: { type: string }) {
function _genImportAttributes(
type: ImportExportType,
options: ESMCodeGenOptions,
) {
// import assertions isn't specified type-only import or export on Typescript
if (type === "import type" || type === "export type") {
return "";
}
// currently, `type` only
if (!assert || typeof assert !== "object") {
return "";

if (typeof options.attributes?.type === "string") {
return ` with { type: ${genString(options.attributes.type)} }`;
}
return ` assert { type: ${genString(assert.type)} }`;
}

export interface DynamicImportOptions extends CodegenOptions {
comment?: string;
wrapper?: boolean;
interopDefault?: boolean;
// https://github.com/tc39/proposal-import-assertions
// https://tc39.es/proposal-import-assertions/
assert?: {
type: string;
};
// TODO: Remove deprecated `assert` in the next major release
if (typeof options.assert?.type === "string") {
return ` assert { type: ${genString(options.assert.type)} }`;
}

return "";
}

export function genDynamicImport(
specifier: string,
options: DynamicImportOptions = {},
Expand All @@ -125,18 +137,24 @@ export function genDynamicImport(
const ineropString = options.interopDefault
? ".then(m => m.default || m)"
: "";
const optionsString = _genDynamicImportOptions(options);
const optionsString = _genDynamicImportAttributes(options);
return `${wrapperString}import(${genString(
specifier,
options,
)}${commentString}${optionsString})${ineropString}`;
}

function _genDynamicImportOptions(options: DynamicImportOptions = {}) {
// currently, `assert` option only
return options.assert && typeof options.assert === "object"
? `, { assert: { type: ${genString(options.assert.type)} } }`
: "";
function _genDynamicImportAttributes(options: DynamicImportOptions = {}) {
// TODO: Remove deprecated `assert` in the next major release
if (typeof options.assert?.type === "string") {
return `, { assert: { type: ${genString(options.assert.type)} } }`;
}

if (typeof options.attributes?.type === "string") {
return `, { with: { type: ${genString(options.attributes.type)} } }`;
}

return "";
}

export function genSafeVariableName(name: string) {
Expand Down
5 changes: 0 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
export interface CodegenOptions {
singleQuotes?: boolean;
// https://github.com/tc39/proposal-import-assertions
// https://tc39.es/proposal-import-assertions/
assert?: {
type: string;
};
}
9 changes: 9 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ const genImportTests = [
code: 'import { foo } from "pkg" assert { type: "json" };',
options: { assert: { type: "json" } },
},
{
names: ["foo"],
code: 'import { foo } from "pkg" with { type: "json" };',
options: { attributes: { type: "json" } },
},
];

describe("genImport", () => {
Expand Down Expand Up @@ -87,6 +92,10 @@ const genDynamicImportTests = [
opts: { assert: { type: "json" } },
code: '() => import("pkg", { assert: { type: "json" } })',
},
{
opts: { attributes: { type: "json" } },
code: '() => import("pkg", { with: { type: "json" } })',
},
];

describe("genDynamicImport", () => {
Expand Down
Loading