From d1b11f123921ccf77f6251ce68f524df84c705f8 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 25 Feb 2025 06:27:02 +0800 Subject: [PATCH] Lazy load plugins in playground (#17119) --- scripts/build-website.js | 48 +++++++++++++++++---------- website/static/service-worker.js | 10 +++--- website/static/worker.js | 57 +++++++++++++++++++++++++++----- 3 files changed, 84 insertions(+), 31 deletions(-) diff --git a/scripts/build-website.js b/scripts/build-website.js index 35c22a175..b859e4e77 100644 --- a/scripts/build-website.js +++ b/scripts/build-website.js @@ -3,10 +3,10 @@ import fs from "node:fs/promises"; import path from "node:path"; import url from "node:url"; -import createEsmUtils from "esm-utils"; +import esbuild from "esbuild"; import { execa } from "execa"; import fastGlob from "fast-glob"; -import { format } from "../src/index.js"; +import serialize from "serialize-javascript"; import { copyFile, DIST_DIR, @@ -16,7 +16,6 @@ import { writeJson, } from "./utils/index.js"; -const { require } = createEsmUtils(import.meta); const runYarn = (command, args, options) => execa("yarn", [command, ...args], { stdout: "inherit", @@ -68,26 +67,39 @@ async function buildPlaygroundFiles() { continue; } - const pluginModule = require(dist); - const plugin = pluginModule.default ?? pluginModule; - const { parsers = {}, printers = {} } = plugin; - packageManifest.builtinPlugins.push({ + const { default: pluginModule } = await import(url.pathToFileURL(dist)); + + const plugin = { + name: path.basename(fileName, ".js"), file: fileName, - parsers: Object.keys(parsers), - printers: Object.keys(printers), - }); + }; + + for (const property of ["languages", "options", "defaultOptions"]) { + const value = pluginModule[property]; + if (value !== undefined) { + plugin[property] = value; + } + } + + for (const property of ["parsers", "printers"]) { + const value = pluginModule[property]; + if (value !== undefined) { + plugin[property] = Object.keys(value); + } + } + + packageManifest.builtinPlugins.push(plugin); } + const code = /* Indent */ ` + "use strict"; + + self.prettierPackageManifest = ${serialize(packageManifest, { space: 2 })}; + `; + await writeFile( path.join(PLAYGROUND_PRETTIER_DIR, "package-manifest.js"), - await format( - /* Indent */ ` - "use strict"; - - const prettierPackageManifest = ${JSON.stringify(packageManifest)}; - `, - { parser: "meriyah" }, - ), + esbuild.transformSync(code, { loader: "js", minify: true }).code.trim(), ); } diff --git a/website/static/service-worker.js b/website/static/service-worker.js index 8130e8404..3c2046b42 100644 --- a/website/static/service-worker.js +++ b/website/static/service-worker.js @@ -1,20 +1,20 @@ -/* global toolbox prettierPackageManifest */ - "use strict"; -importScripts("lib/package-manifest.js"); importScripts( + "lib/package-manifest.js", "https://cdnjs.cloudflare.com/ajax/libs/sw-toolbox/3.6.0/sw-toolbox.js", ); -const plugins = prettierPackageManifest.builtinPlugins.map( +const { toolbox, prettierPackageManifest } = self; +const pluginFiles = prettierPackageManifest.builtinPlugins.map( ({ file }) => `lib/${file}`, ); toolbox.precache([ // Scripts "lib/standalone.js", - ...plugins, + "lib/package-manifest.js", + ...pluginFiles, "playground.js", "https://cdnjs.cloudflare.com/ajax/libs/sw-toolbox/3.6.0/sw-toolbox.js", diff --git a/website/static/worker.js b/website/static/worker.js index 0ce0ecacd..f97d42654 100644 --- a/website/static/worker.js +++ b/website/static/worker.js @@ -1,13 +1,49 @@ -/* globals prettier prettierPlugins prettierPackageManifest */ - "use strict"; -importScripts("lib/package-manifest.js"); -importScripts("lib/standalone.js"); +importScripts("lib/package-manifest.js", "lib/standalone.js"); -// TODO[@fisker]: Lazy load plugins -for (const { file } of prettierPackageManifest.builtinPlugins) { - importScripts(`lib/${file}`); +const { prettier, prettierPackageManifest } = self; + +const importedPlugins = new Map(); +function importPlugin(plugin) { + if (!importedPlugins.has(plugin)) { + importScripts(`lib/${plugin.file}`); + + const module = globalThis.prettierPlugins[plugin.name]; + + if (!module) { + throw new Error(`Load plugin '${plugin.file}' failed.`); + } + + importedPlugins.set(plugin, module); + } + + return importedPlugins.get(plugin); +} + +function createPlugin(pluginManifest) { + const { languages, options, defaultOptions } = pluginManifest; + const [parsers, printers] = ["parsers", "printers"].map((property) => + Array.isArray(pluginManifest[property]) + ? Object.defineProperties( + Object.create(null), + Object.fromEntries( + pluginManifest[property].map((parserName) => [ + parserName, + { + configurable: true, + enumerable: true, + get() { + return importPlugin(pluginManifest)[property][parserName]; + }, + }, + ]), + ), + ) + : undefined, + ); + + return { languages, options, defaultOptions, parsers, printers }; } const docExplorerPlugin = { @@ -29,7 +65,12 @@ const docExplorerPlugin = { languages: [{ name: "doc-explorer", parsers: ["doc-explorer"] }], }; -const plugins = [...Object.values(prettierPlugins), docExplorerPlugin]; +const plugins = [ + ...prettierPackageManifest.builtinPlugins.map((plugin) => + createPlugin(plugin), + ), + docExplorerPlugin, +]; self.onmessage = async function (event) { self.postMessage({