Skip to content

Commit

Permalink
feat: add .js extension for mtc output
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Dec 16, 2023
1 parent 25649a1 commit 842b5ba
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .changeset/gorgeous-ducks-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@marko/language-tools": minor
"@marko/type-check": minor
---

Automatically add `.js` extensions where necessary in files output by `@marko/type-check` to work better with native es modules.
2 changes: 1 addition & 1 deletion packages/language-tools/src/processors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface PrintContext {

export const extensions = [marko.extension] as ProcessorExtension[];

export function create(options: Parameters<ProcessorConfig["create"]>[0]) {
export function create(options: CreateProcessorOptions) {
return {
[marko.extension]: marko.create(options),
} as Record<ProcessorExtension, Processor>;
Expand Down
42 changes: 41 additions & 1 deletion packages/language-tools/src/processors/marko.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import path from "path";
import type { Config, types as t } from "@marko/compiler";
import type ts from "typescript/lib/tsserverlibrary";
import { ScriptLang, extractScript } from "../extractors/script";
import { parse } from "../parser";

import * as Project from "../util/project";
import type { ProcessorConfig } from ".";

const isRemapExtensionReg = /\.ts$/;
const skipRemapExtensionsReg =
/\.(?:[cm]?jsx?|json|marko|css|less|sass|scss|styl|stylus|pcss|postcss|sss|a?png|jpe?g|jfif|pipeg|pjp|gif|svg|ico|web[pm]|avif|mp4|ogg|mp3|wav|flac|aac|opus|woff2?|eot|[ot]tf|webmanifest|pdf|txt)$/;

export default {
extension: ".marko",
create({ ts, host, configFile }) {
Expand All @@ -21,14 +26,49 @@ export default {
runtimeTypes.internalTypesFile,
runtimeTypes.markoTypesFile,
];
const compileConfig: import("@marko/compiler").Config = {
const getJSFileIfTSExists = (source: string, importer: string) =>
host.fileExists(path.join(importer, "..", `${source}.ts`)) &&
`${source}.js`;
const compileConfig: Config = {
output: "source",
stripTypes: true,
sourceMaps: true,
babelConfig: {
babelrc: false,
configFile: false,
browserslistConfigFile: false,
plugins: [
{
visitor: {
// Find all relative imports in Marko template
// if they would map to a `.ts` file, then we convert it to a `.js` file for the output.
"ImportDeclaration|ExportNamedDeclaration"(
decl: t.NodePath<
t.ImportDeclaration | t.ExportNamedDeclaration
>,
) {
const { node } = decl;
const value = node.source?.value;
const importKind =
"importKind" in node ? node.importKind : undefined;
if (
value?.[0] === "." &&
(!importKind || importKind === "value") &&
!skipRemapExtensionsReg.test(value)
) {
const filename = decl.hub.file.opts.filename as string;
const remap = isRemapExtensionReg.test(value)
? `${value.slice(0, -2)}js`
: getJSFileIfTSExists(value, filename) ||
getJSFileIfTSExists(`${value}/index`, filename);
if (remap) {
node.source!.value = remap;
}
}
},
},
},
],
caller: {
name: "@marko/type-check",
supportsStaticESM: true,
Expand Down
62 changes: 60 additions & 2 deletions packages/type-check/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const getCanonicalFileName = ts.sys.useCaseSensitiveFileNames
: (fileName: string) => fileName.toLowerCase();
const fsPathReg = /^(?:[./\\]|[A-Z]:)/i;
const modulePartsReg = /^((?:@(?:[^/]+)\/)?(?:[^/]+))(.*)$/;
const isRemapExtensionReg = /\.ts$/;
const skipRemapExtensionsReg =
/\.(?:[cm]?jsx?|json|marko|css|less|sass|scss|styl|stylus|pcss|postcss|sss|a?png|jpe?g|jfif|pipeg|pjp|gif|svg|ico|web[pm]|avif|mp4|ogg|mp3|wav|flac|aac|opus|woff2?|eot|[ot]tf|webmanifest|pdf|txt)$/;

const extractCache = new WeakMap<
ts.SourceFile,
ReturnType<Processors.Processor["extract"]>
Expand Down Expand Up @@ -95,6 +99,61 @@ export default function run(opts: Options) {
getCanonicalFileName,
options,
);
const getJSFileIfTSExists = (source: string, importer: string) =>
compilerHost.fileExists(path.join(importer, "..", `${source}.ts`)) &&
`${source}.js`;

// Find all relative imports in typescript output
// if they would map to a `.ts` file, then we convert it to a `.js` file for the output.
const customTransformers: ts.CustomTransformers = {
after: [
(ctx) => (sourceFile) => {
return ts.visitNode(sourceFile, visit) as ts.SourceFile;

function visit(node: ts.Node): ts.Node {
if (ts.isSourceFile(node)) {
return ts.visitEachChild(node, visit, ctx);
}

if (
(ts.isImportDeclaration(node) ||
ts.isExportDeclaration(node)) &&
node.moduleSpecifier &&
ts.isStringLiteral(node.moduleSpecifier)
) {
const value = node.moduleSpecifier.text;
if (value[0] === "." && !skipRemapExtensionsReg.test(value)) {
const { fileName } = sourceFile;
const remap = isRemapExtensionReg.test(value)
? `${value.slice(0, -2)}js`
: getJSFileIfTSExists(value, fileName) ||
getJSFileIfTSExists(`${value}/index`, fileName);
if (remap) {
return ts.isImportDeclaration(node)
? ctx.factory.updateImportDeclaration(
node,
node.modifiers,
node.importClause,
ctx.factory.createStringLiteral(remap),
node.attributes,
)
: ctx.factory.updateExportDeclaration(
node,
node.modifiers,
node.isTypeOnly,
node.exportClause,
ctx.factory.createStringLiteral(remap),
node.attributes,
);
}
}
}

return node;
}
},
],
};

const { readDirectory = ts.sys.readDirectory } = compilerHost;
compilerHost.readDirectory = (
Expand Down Expand Up @@ -138,7 +197,7 @@ export default function run(opts: Options) {
resolvedFileName = path.resolve(containingFile, "..", moduleName);
} else {
// For other paths we treat it as a node_module and try resolving
// that modules `marko.json`. If the `marko.json` exists then we'll
// that modules `package.json`. If the `package.json` exists then we'll
// try resolving the `.marko` file relative to that.
const [, nodeModuleName, relativeModulePath] =
modulePartsReg.exec(moduleName)!;
Expand Down Expand Up @@ -285,7 +344,6 @@ export default function run(opts: Options) {
_writeFile,
cancellationToken,
emitOnlyDtsFiles,
customTransformers,
) => {
let writeFile = _writeFile;
if (_writeFile) {
Expand Down

0 comments on commit 842b5ba

Please sign in to comment.