UNPKG

@greenwood/cli

Version:
365 lines (321 loc) 12.3 kB
// @ts-nocheck import fs from "fs/promises"; import { checkResourceExists } from "../lib/resource-utils.js"; const cwd = new URL(`file://${process.cwd()}/`); const greenwoodPluginsDirectoryUrl = new URL("../plugins/", import.meta.url); const PLUGINS_FLATTENED_DEPTH = 2; // get and "tag" all plugins provided / maintained by the @greenwood/cli // and include as the default set, with all user plugins getting appended const greenwoodPlugins = ( await Promise.all( [ new URL("./copy/", greenwoodPluginsDirectoryUrl), new URL("./renderer/", greenwoodPluginsDirectoryUrl), new URL("./resource/", greenwoodPluginsDirectoryUrl), new URL("./server/", greenwoodPluginsDirectoryUrl), ].map(async (pluginDirectoryUrl) => { const files = await fs.readdir(pluginDirectoryUrl); return await Promise.all( files.map(async (file) => { const importUrl = new URL(`./${file}`, pluginDirectoryUrl); // @ts-expect-error see https://github.com/microsoft/TypeScript/issues/42866 const pluginImport = await import(importUrl); const plugin = pluginImport[Object.keys(pluginImport)[0]]; return Array.isArray(plugin) ? plugin : [plugin]; }), ); }), ) ) .flat(PLUGINS_FLATTENED_DEPTH) .map((plugin) => { const isStandardStaticResource = (plugin.name.startsWith("plugin-standard") && plugin.name !== "plugin-standard-html") || plugin.name === "plugin-source-maps"; return { isGreenwoodDefaultPlugin: true, isStandardStaticResource, ...plugin, }; }); const optimizations = ["default", "none", "static", "inline"]; const pluginTypes = [ "copy", "context", "resource", "rollup", "server", "source", "renderer", "adapter", ]; const defaultConfig = { activeContent: false, basePath: "", devServer: { hud: true, port: 1984, extensions: [], proxy: {}, }, isolation: false, layoutsDirectory: "layouts", markdown: { plugins: [] }, optimization: optimizations[0], pagesDirectory: "pages", plugins: greenwoodPlugins, polyfills: { importAttributes: null, // or ['css', 'json'] importMaps: false, }, port: 8080, prerender: false, useTsc: false, workspace: new URL("./src/", cwd), }; const readAndMergeConfig = async () => { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { try { // check for greenwood.config.ts or greenwood.config.js const jsConfigUrl = new URL("./greenwood.config.js", cwd); const tsConfigUrl = new URL("./greenwood.config.ts", cwd); const configUrl = (await checkResourceExists(tsConfigUrl)) ? tsConfigUrl : (await checkResourceExists(jsConfigUrl)) ? jsConfigUrl : null; // deep clone of default config let customConfig = Object.assign({}, defaultConfig); let isSPA; // check for SPA if (await checkResourceExists(new URL("./index.html", customConfig.workspace))) { isSPA = true; } if (configUrl) { // should try and figure this out - https://github.com/ProjectEvergreen/greenwood/issues/1439 // console.log(`Configuration file detected... loading => ${configUrl.href}`); // @ts-expect-error see https://github.com/microsoft/TypeScript/issues/42866 const userCfgFile = (await import(configUrl)).default; const { workspace, devServer, markdown, optimization, plugins, port, prerender, basePath, staticRouter, pagesDirectory, layoutsDirectory, activeContent, isolation, polyfills, useTsc, } = userCfgFile; // workspace validation if (workspace) { if (!(workspace instanceof URL)) { reject("Configuration error: workspace must be an instance of URL"); } if (await checkResourceExists(workspace)) { customConfig.workspace = workspace; } else { reject( "Configuration error: Workspace doesn't exist! Please double check your configuration.", ); } } if ( typeof optimization === "string" && optimizations.indexOf(optimization.toLowerCase()) >= 0 ) { customConfig.optimization = optimization; } else if (optimization) { reject( `Configuration error: provided optimization "${optimization}" is not supported. Please use one of: ${optimizations.join(", ")}.`, ); } if (activeContent) { if (typeof activeContent !== "boolean") { reject("Configuration error: activeContent must be a boolean"); } customConfig.activeContent = activeContent; } if (plugins && plugins.length > 0) { const flattened = plugins.flat(PLUGINS_FLATTENED_DEPTH); flattened.forEach((plugin) => { if (!plugin.type || pluginTypes.indexOf(plugin.type) < 0) { reject( `Configuration error: plugins must be one of type "${pluginTypes.join(", ")}". got "${plugin.type}" instead.`, ); } if (!plugin.provider || typeof plugin.provider !== "function") { const providerTypeof = typeof plugin.provider; reject( `Configuration error: plugins provider must be a function. got ${providerTypeof} instead.`, ); } if (!plugin.name || typeof plugin.name !== "string") { const nameTypeof = typeof plugin.name; reject(`Configuration error: plugins must have a name. got ${nameTypeof} instead.`); } }); // if user provided a custom renderer, filter out Greenwood's default renderer const customRendererPlugins = flattened.filter( (plugin) => plugin.type === "renderer", ).length; if (customRendererPlugins === 1) { customConfig.plugins = customConfig.plugins.filter((plugin) => { return plugin.type !== "renderer"; }); } else if (customRendererPlugins > 1) { console.warn( "Configuration warning: more than one custom renderer plugin detected. Please make sure you are only loading one.", ); console.debug(plugins.filter((plugin) => plugin.type === "renderer")); } customConfig.plugins = [...customConfig.plugins, ...flattened]; } if (devServer && Object.keys(devServer).length > 0) { if (Object.prototype.hasOwnProperty.call(devServer, "hud")) { if (typeof devServer.hud === "boolean") { customConfig.devServer.hud = devServer.hud; } else { reject( `Configuration error: devServer hud options must be a boolean. Passed value was: ${devServer.hud}`, ); } } if (devServer.port) { if (!Number.isInteger(devServer.port)) { reject( `Configuration error: devServer port must be an integer. Passed value was: ${devServer.port}`, ); } else { customConfig.devServer.port = devServer.port; } } if (devServer.proxy) { customConfig.devServer.proxy = devServer.proxy; } if (devServer.extensions) { if (Array.isArray(devServer.extensions)) { customConfig.devServer.extensions = devServer.extensions; } else { reject( "Configuration error: provided extensions is not an array. Please provide an array like ['txt', 'foo']", ); } } } if (markdown && Object.keys(markdown).length > 0) { customConfig.markdown.plugins = markdown.plugins && markdown.plugins.length > 0 ? markdown.plugins : []; } if (port) { if (!Number.isInteger(port)) { reject(`Configuration error: port must be an integer. Passed value was: ${port}`); } else { customConfig.port = port; } } if (basePath) { if (typeof basePath !== "string") { reject( `Configuration error: basePath must be a string. Passed value was: ${basePath}`, ); } else { customConfig.basePath = basePath; } } if (pagesDirectory && typeof pagesDirectory === "string") { customConfig.pagesDirectory = pagesDirectory; } else if (pagesDirectory) { reject( `Configuration error: provided pagesDirectory "${pagesDirectory}" is not supported. Please make sure to pass something like 'docs/'`, ); } if (layoutsDirectory && typeof layoutsDirectory === "string") { customConfig.layoutsDirectory = layoutsDirectory; } else if (layoutsDirectory) { reject( `Configuration error: provided layoutsDirectory "${layoutsDirectory}" is not supported. Please make sure to pass something like 'layouts/'`, ); } if (prerender !== undefined) { if (typeof prerender === "boolean") { customConfig.prerender = prerender; } else { reject( `Configuration error: prerender must be a boolean; true or false. Passed value was typeof: ${typeof prerender}`, ); } } // SPA should _not_ prerender unless if user has specified prerender should be true if (prerender === undefined && isSPA) { customConfig.prerender = false; } if (isolation !== undefined) { if (typeof isolation === "boolean") { customConfig.isolation = isolation; } else { reject( `Configuration error: isolation must be a boolean; true or false. Passed value was typeof: ${typeof staticRouter}`, ); } } if (staticRouter !== undefined) { if (typeof staticRouter === "boolean") { customConfig.staticRouter = staticRouter; } else { reject( `Configuration error: staticRouter must be a boolean; true or false. Passed value was typeof: ${typeof staticRouter}`, ); } } if (polyfills !== undefined) { const { importMaps, importAttributes } = polyfills; customConfig.polyfills = { importAttributes: null, importMaps: false }; if (importMaps) { if (typeof importMaps === "boolean") { customConfig.polyfills.importMaps = true; } else { reject( `Configuration error: polyfills.importMaps must be a boolean; true or false. Passed value was typeof: ${typeof importMaps}`, ); } } if (importAttributes) { if (Array.isArray(importAttributes)) { customConfig.polyfills.importAttributes = importAttributes; } else { reject( `Configuration error: polyfills.importAttributes must be an array of types; ['css', 'json']. Passed value was typeof: ${typeof importAttributes}`, ); } } } if (useTsc !== undefined) { if (typeof useTsc === "boolean") { customConfig.useTsc = useTsc; } else { reject( `Configuration error: useTsc must be a boolean; true or false. Passed value was typeof: ${typeof useTsc}`, ); } } } else { // SPA should _not_ prerender unless if user has specified prerender should be true if (isSPA) { customConfig.prerender = false; } } resolve({ ...defaultConfig, ...customConfig }); } catch (err) { reject(err); } }); }; export { readAndMergeConfig };