fix: switch from tsc watch to chokidar (#11110)

This commit is contained in:
Harminder Virk authored and GitHub committed 2025-01-23 19:12:07 +05:30
1 parent 31384855af
commit 0deffe7b9b
5 files changed
+133 -77

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/medusa": patch
"@medusajs/framework": patch
---
fix: switch from tsc watch to chokidar
+1
View File
@@ -82,6 +82,7 @@
"@medusajs/workflows-sdk": "~2.3.1", "@medusajs/workflows-sdk": "~2.3.1",
"@opentelemetry/api": "^1.9.0", "@opentelemetry/api": "^1.9.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"chokidar": "^3.4.2",
"compression": "1.7.4", "compression": "1.7.4",
"connect-redis": "5.2.0", "connect-redis": "5.2.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
@@ -1,8 +1,9 @@
import path from "path" import path from "path"
import { getConfigFile } from "@medusajs/utils" import chokidar from "chokidar"
import type { AdminOptions, ConfigModule, Logger } from "@medusajs/types"
import { rm, access, constants, copyFile, writeFile, mkdir } from "fs/promises"
import type tsStatic from "typescript" import type tsStatic from "typescript"
import { FileSystem, getConfigFile } from "@medusajs/utils"
import { rm, access, constants, copyFile } from "fs/promises"
import type { AdminOptions, ConfigModule, Logger } from "@medusajs/types"
/** /**
* The compiler exposes the opinionated APIs for compiling Medusa * The compiler exposes the opinionated APIs for compiling Medusa
@@ -27,7 +28,6 @@ export class Compiler {
#adminSourceFolder: string #adminSourceFolder: string
#pluginsDistFolder: string #pluginsDistFolder: string
#backendIgnoreFiles: string[] #backendIgnoreFiles: string[]
#pluginOptionsPath: string
#adminOnlyDistFolder: string #adminOnlyDistFolder: string
#tsCompiler?: typeof tsStatic #tsCompiler?: typeof tsStatic
@@ -38,10 +38,6 @@ export class Compiler {
this.#adminSourceFolder = path.join(this.#projectRoot, "src/admin") this.#adminSourceFolder = path.join(this.#projectRoot, "src/admin")
this.#adminOnlyDistFolder = path.join(this.#projectRoot, ".medusa/admin") this.#adminOnlyDistFolder = path.join(this.#projectRoot, ".medusa/admin")
this.#pluginsDistFolder = path.join(this.#projectRoot, ".medusa/server") this.#pluginsDistFolder = path.join(this.#projectRoot, ".medusa/server")
this.#pluginOptionsPath = path.join(
this.#projectRoot,
".medusa/server/medusa-plugin-options.json"
)
this.#backendIgnoreFiles = [ this.#backendIgnoreFiles = [
"integration-tests", "integration-tests",
"test", "test",
@@ -141,13 +137,14 @@ export class Compiler {
} }
/** /**
* Ensures a directory exists * Returns a boolean indicating if a file extension belongs
* to a JavaScript or TypeScript file
*/ */
async #ensureDir(path: string, clean?: boolean) { #isScriptFile(filePath: string) {
if (clean) { if (filePath.endsWith(".ts") && !filePath.endsWith(".d.ts")) {
await this.#clean(path) return true
} }
await mkdir(path, { recursive: true }) return filePath.endsWith(".js")
} }
/** /**
@@ -167,24 +164,6 @@ export class Compiler {
return { configFilePath, configModule } return { configFilePath, configModule }
} }
/**
* Creates medusa-plugin-options.json file that contains some
* metadata related to the plugin, which could be helpful
* for MedusaJS loaders during development
*/
async #createPluginOptionsFile() {
await writeFile(
this.#pluginOptionsPath,
JSON.stringify(
{
srcDir: path.join(this.#projectRoot, "src"),
},
null,
2
)
)
}
/** /**
* Prints typescript diagnostic messages * Prints typescript diagnostic messages
*/ */
@@ -437,7 +416,7 @@ export class Compiler {
*/ */
const { emitResult, diagnostics } = await this.#emitBuildOutput( const { emitResult, diagnostics } = await this.#emitBuildOutput(
tsConfig, tsConfig,
["integration-tests", "test", "unit-tests", "src/admin"], this.#backendIgnoreFiles,
dist dist
) )
@@ -472,54 +451,71 @@ export class Compiler {
* The "onFileChange" argument can be used to get notified when * The "onFileChange" argument can be used to get notified when
* a file has changed. * a file has changed.
*/ */
async developPluginBackend(onFileChange?: () => void) { async developPluginBackend(
await this.#ensureDir(this.#pluginsDistFolder, true) transformer: (filePath: string) => Promise<string>,
await this.#createPluginOptionsFile() onFileChange?: (
const ts = await this.#loadTSCompiler() filePath: string,
action: "add" | "change" | "unlink"
) => void
) {
const fs = new FileSystem(this.#pluginsDistFolder)
await fs.createJson("medusa-plugin-options.json", {
srcDir: path.join(this.#projectRoot, "src"),
})
/** const watcher = chokidar.watch(["."], {
* Format host is needed to print diagnostic messages ignoreInitial: true,
*/ cwd: this.#projectRoot,
const formatHost: tsStatic.FormatDiagnosticsHost = { ignored: [
getCanonicalFileName: (path) => path, /(^|[\\/\\])\../,
getCurrentDirectory: ts.sys.getCurrentDirectory, "node_modules",
getNewLine: () => ts.sys.newLine, "dist",
} "static",
"private",
".medusa/**/*",
...this.#backendIgnoreFiles,
],
})
/** watcher.on("add", async (file) => {
* Creating a watcher compiler host to watch files and recompile if (!this.#isScriptFile(file)) {
* them as they are changed return
*/
const host = ts.createWatchCompilerHost(
this.#tsConfigPath,
{
outDir: this.#pluginsDistFolder,
noCheck: true,
},
ts.sys,
ts.createEmitAndSemanticDiagnosticsBuilderProgram,
(diagnostic) => this.#printDiagnostics(ts, [diagnostic]),
(diagnostic) => {
if (typeof diagnostic.messageText === "string") {
this.#logger.info(diagnostic.messageText)
} else {
this.#logger.info(
ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost)
)
}
},
{
excludeDirectories: this.#backendIgnoreFiles,
} }
) const relativePath = path.relative(this.#projectRoot, file)
const outputPath = relativePath.replace(/\.ts$/, ".js")
const origPostProgramCreate = host.afterProgramCreate this.#logger.info(`${relativePath} updated: Republishing changes`)
host.afterProgramCreate = (program) => { await fs.create(outputPath, await transformer(file))
origPostProgramCreate!(program)
onFileChange?.()
}
ts.createWatchProgram(host) onFileChange?.(file, "add")
})
watcher.on("change", async (file) => {
if (!this.#isScriptFile(file)) {
return
}
const relativePath = path.relative(this.#projectRoot, file)
const outputPath = relativePath.replace(/\.ts$/, ".js")
this.#logger.info(`${relativePath} updated: Republishing changes`)
await fs.create(outputPath, await transformer(file))
onFileChange?.(file, "change")
})
watcher.on("unlink", async (file) => {
if (!this.#isScriptFile(file)) {
return
}
const relativePath = path.relative(this.#projectRoot, file)
const outputPath = relativePath.replace(/\.ts$/, ".js")
this.#logger.info(`${relativePath} removed: Republishing changes`)
await fs.remove(outputPath)
onFileChange?.(file, "unlink")
})
watcher.on("ready", () => {
this.#logger.info("watching for file changes")
})
} }
async buildPluginAdminExtensions(bundler: { async buildPluginAdminExtensions(bundler: {
+54 -2
View File
@@ -1,4 +1,5 @@
import path from "path" import path from "path"
import * as swcCore from "@swc/core"
import { execFile } from "child_process" import { execFile } from "child_process"
import { logger } from "@medusajs/framework/logger" import { logger } from "@medusajs/framework/logger"
import { Compiler } from "@medusajs/framework/build-tools" import { Compiler } from "@medusajs/framework/build-tools"
@@ -10,9 +11,18 @@ export default async function developPlugin({
}) { }) {
let isBusy = false let isBusy = false
const compiler = new Compiler(directory, logger) const compiler = new Compiler(directory, logger)
const parsedConfig = await compiler.loadTSConfigFile()
if (!parsedConfig) {
return
}
const yalcBin = path.join(path.dirname(require.resolve("yalc")), "yalc.js") const yalcBin = path.join(path.dirname(require.resolve("yalc")), "yalc.js")
await compiler.developPluginBackend(async () => { /**
* Publishes the build output to the registry and updates
* installations
*/
function publishChanges() {
/** /**
* Here we avoid multiple publish calls when the filesystem is * Here we avoid multiple publish calls when the filesystem is
* changed too quickly. This might result in stale content in * changed too quickly. This might result in stale content in
@@ -46,5 +56,47 @@ export default async function developPlugin({
console.error(stderr) console.error(stderr)
} }
) )
}) }
/**
* Transforms a given file using @swc/core
*/
async function transformFile(filePath: string) {
const output = await swcCore.transformFile(filePath, {
sourceMaps: "inline",
module: {
type: "commonjs",
strictMode: true,
noInterop: false,
},
jsc: {
externalHelpers: false,
target: "es2021",
parser: {
syntax: "typescript",
tsx: true,
decorators: true,
dynamicImport: true,
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
react: {
throwIfNamespace: false,
useBuiltins: false,
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
importSource: "react",
runtime: "automatic",
},
},
keepClassNames: true,
baseUrl: directory,
},
})
return output.code
}
await compiler.buildPluginBackend(parsedConfig)
await compiler.developPluginBackend(transformFile, publishChanges)
} }
+1
View File
@@ -5766,6 +5766,7 @@ __metadata:
"@types/express": ^4.17.17 "@types/express": ^4.17.17
"@types/jsonwebtoken": ^8.5.9 "@types/jsonwebtoken": ^8.5.9
awilix: ^8.0.1 awilix: ^8.0.1
chokidar: ^3.4.2
compression: 1.7.4 compression: 1.7.4
connect-redis: 5.2.0 connect-redis: 5.2.0
cookie-parser: ^1.4.6 cookie-parser: ^1.4.6