diff --git a/.flowconfig b/.flowconfig index c777bb6ddb..1fec814349 100644 --- a/.flowconfig +++ b/.flowconfig @@ -19,6 +19,7 @@ untyped-type-import=warn [options] suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe include_warnings=true +module.ignore_non_literal_requires=true [version] ^0.66.0 diff --git a/src/config.js b/src/config.js index 2fdf5ff162..fe902bf94c 100644 --- a/src/config.js +++ b/src/config.js @@ -5,6 +5,7 @@ import type {Reporter} from './reporters/index.js'; import type {Manifest, PackageRemote, WorkspacesManifestMap, WorkspacesConfig} from './types.js'; import type PackageReference from './package-reference.js'; import {execFromManifest} from './util/execute-lifecycle-script.js'; +import {checkImportScripts} from './util/script-utils'; import {resolveWithHome} from './util/path.js'; import {boolifyWithDefault} from './util/conversion.js'; import normalizeManifest from './util/normalize-manifest/index.js'; @@ -747,6 +748,7 @@ export default class Config { const data = await this.readJson(loc); data._registry = registry; data._loc = loc; + data.scripts = await checkImportScripts(data.scripts, this.reporter); return normalizeManifest(data, dir, this, isRoot); } else { return null; diff --git a/src/util/script-utils.js b/src/util/script-utils.js new file mode 100644 index 0000000000..c50ef1f93a --- /dev/null +++ b/src/util/script-utils.js @@ -0,0 +1,52 @@ +/* @flow */ + +import * as fs from './fs.js'; +import type {Reporter} from '../reporters/index.js'; + +export async function checkImportScripts(scripts: any, reporter: Reporter): Promise { + if (typeof scripts === 'string' && scripts.length > 0) { + if (await fs.exists(scripts)) { + const module = require(await fs.realpath(scripts)); + if (module && typeof module.scripts === 'object') { + scripts = iterateScripts(module.scripts, '', module.delimiter, reporter); + } else { + reporter && + reporter.warn( + `Invalid scripts module: ${scripts}. The module must be exported and include a root scripts object.`, + ); + } + } + } + return scripts; +} + +function iterateScripts(node: Object, path: string = '', delim: string = '.', reporter: Reporter): Object { + const scripts = {}; + + const addScript = (key, script) => { + scripts[key] && + reporter && + reporter.warn(`Duplicate script key detected: ${key}. Scripts should be structured to have unique keys.`); + scripts[key] = script; + }; + + if (node['script'] && typeof node['script'] === 'string') { + addScript(path, node['script']); // Add script, ignore other non object keys + } else { + Object.keys(node).filter(k => typeof node[k] === 'string').forEach(k => { + if (k === 'default') { + addScript(path, node[k]); + } else { + addScript([path, k].filter(Boolean).join(delim), node[k]); + } + }); + } + + // Process remaining object nodes + Object.keys(node).filter(k => typeof node[k] === 'object').forEach(k => { + const nodepath = k === 'default' ? path : [path, k].filter(Boolean).join(delim); + const iteratedScripts = iterateScripts(node[k], nodepath, delim, reporter); + Object.keys(iteratedScripts).forEach(k => addScript(k, iteratedScripts[k])); + }); + return scripts; +}