Support extending ESM based configurations (#3037)
Co-authored-by: Matt Travi <programmer@travi.org>
This commit is contained in:
		
							parent
							
								
									250e7ae2c5
								
							
						
					
					
						commit
						6900865324
					
				| @ -1,6 +1,5 @@ | |||||||
| import { dirname, resolve } from "node:path"; | import { dirname, extname } from "node:path"; | ||||||
| import { fileURLToPath } from "node:url"; | import { fileURLToPath } from "node:url"; | ||||||
| import { createRequire } from "node:module"; |  | ||||||
| 
 | 
 | ||||||
| import { castArray, isNil, isPlainObject, isString, pickBy } from "lodash-es"; | import { castArray, isNil, isPlainObject, isString, pickBy } from "lodash-es"; | ||||||
| import { readPackageUp } from "read-pkg-up"; | import { readPackageUp } from "read-pkg-up"; | ||||||
| @ -14,7 +13,6 @@ import { parseConfig, validatePlugin } from "./plugins/utils.js"; | |||||||
| 
 | 
 | ||||||
| const debug = debugConfig("semantic-release:config"); | const debug = debugConfig("semantic-release:config"); | ||||||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | const __dirname = dirname(fileURLToPath(import.meta.url)); | ||||||
| const require = createRequire(import.meta.url); |  | ||||||
| 
 | 
 | ||||||
| const CONFIG_NAME = "release"; | const CONFIG_NAME = "release"; | ||||||
| 
 | 
 | ||||||
| @ -35,7 +33,17 @@ export default async (context, cliOptions) => { | |||||||
|     options = { |     options = { | ||||||
|       ...(await castArray(extendPaths).reduce(async (eventualResult, extendPath) => { |       ...(await castArray(extendPaths).reduce(async (eventualResult, extendPath) => { | ||||||
|         const result = await eventualResult; |         const result = await eventualResult; | ||||||
|         const extendsOptions = require(resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath)); |         const resolvedPath = resolveFrom.silent(__dirname, extendPath) || resolveFrom(cwd, extendPath); | ||||||
|  |         const importAssertions = | ||||||
|  |           extname(resolvedPath) === ".json" | ||||||
|  |             ? { | ||||||
|  |                 assert: { | ||||||
|  |                   type: "json", | ||||||
|  |                 }, | ||||||
|  |               } | ||||||
|  |             : undefined; | ||||||
|  | 
 | ||||||
|  |         const { default: extendsOptions } = await import(resolvedPath, importAssertions); | ||||||
| 
 | 
 | ||||||
|         // For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
 |         // For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
 | ||||||
|         // so those plugin will be loaded relative to the config file
 |         // so those plugin will be loaded relative to the config file
 | ||||||
|  | |||||||
| @ -389,6 +389,92 @@ test.serial('Read configuration from an array of paths in "extends"', async (t) | |||||||
|   t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig }); |   t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | test.serial('Read configuration from an array of CJS files in "extends"', async (t) => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   const { cwd } = await gitRepo(); | ||||||
|  |   const pkgOptions = { extends: ["./shareable1.cjs", "./shareable2.cjs"] }; | ||||||
|  |   const options1 = { | ||||||
|  |     verifyRelease: "verifyRelease1", | ||||||
|  |     analyzeCommits: { path: "analyzeCommits1", param: "analyzeCommits_param1" }, | ||||||
|  |     branches: ["test_branch"], | ||||||
|  |     repositoryUrl: "https://host.null/owner/module.git", | ||||||
|  |   }; | ||||||
|  |   const options2 = { | ||||||
|  |     verifyRelease: "verifyRelease2", | ||||||
|  |     generateNotes: "generateNotes2", | ||||||
|  |     analyzeCommits: { path: "analyzeCommits2", param: "analyzeCommits_param2" }, | ||||||
|  |     branches: ["test_branch"], | ||||||
|  |     tagFormat: `v\${version}`, | ||||||
|  |     plugins: false, | ||||||
|  |   }; | ||||||
|  |   // Create package.json and shareable.json in repository root
 | ||||||
|  |   await outputJson(path.resolve(cwd, "package.json"), { release: pkgOptions }); | ||||||
|  |   await writeFile(path.resolve(cwd, "shareable1.cjs"), `module.exports = ${JSON.stringify(options1)}`); | ||||||
|  |   await writeFile(path.resolve(cwd, "shareable2.cjs"), `module.exports = ${JSON.stringify(options2)}`); | ||||||
|  |   const expectedOptions = { ...options1, ...options2, branches: ["test_branch"] }; | ||||||
|  |   // Verify the plugins module is called with the plugin options from shareable1.mjs and shareable2.mjs
 | ||||||
|  |   td.when( | ||||||
|  |     plugins( | ||||||
|  |       { options: expectedOptions, cwd }, | ||||||
|  |       { | ||||||
|  |         verifyRelease1: "./shareable1.cjs", | ||||||
|  |         verifyRelease2: "./shareable2.cjs", | ||||||
|  |         generateNotes2: "./shareable2.cjs", | ||||||
|  |         analyzeCommits1: "./shareable1.cjs", | ||||||
|  |         analyzeCommits2: "./shareable2.cjs", | ||||||
|  |       } | ||||||
|  |     ) | ||||||
|  |   ).thenResolve(pluginsConfig); | ||||||
|  | 
 | ||||||
|  |   const result = await t.context.getConfig({ cwd }); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from shareable1.json and shareable2.json
 | ||||||
|  |   t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('Read configuration from an array of ESM files in "extends"', async (t) => { | ||||||
|  |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|  |   const { cwd } = await gitRepo(); | ||||||
|  |   const pkgOptions = { extends: ["./shareable1.mjs", "./shareable2.mjs"] }; | ||||||
|  |   const options1 = { | ||||||
|  |     verifyRelease: "verifyRelease1", | ||||||
|  |     analyzeCommits: { path: "analyzeCommits1", param: "analyzeCommits_param1" }, | ||||||
|  |     branches: ["test_branch"], | ||||||
|  |     repositoryUrl: "https://host.null/owner/module.git", | ||||||
|  |   }; | ||||||
|  |   const options2 = { | ||||||
|  |     verifyRelease: "verifyRelease2", | ||||||
|  |     generateNotes: "generateNotes2", | ||||||
|  |     analyzeCommits: { path: "analyzeCommits2", param: "analyzeCommits_param2" }, | ||||||
|  |     branches: ["test_branch"], | ||||||
|  |     tagFormat: `v\${version}`, | ||||||
|  |     plugins: false, | ||||||
|  |   }; | ||||||
|  |   // Create package.json and shareable.json in repository root
 | ||||||
|  |   await outputJson(path.resolve(cwd, "package.json"), { release: pkgOptions }); | ||||||
|  |   await writeFile(path.resolve(cwd, "shareable1.mjs"), `export default ${JSON.stringify(options1)}`); | ||||||
|  |   await writeFile(path.resolve(cwd, "shareable2.mjs"), `export default ${JSON.stringify(options2)}`); | ||||||
|  |   const expectedOptions = { ...options1, ...options2, branches: ["test_branch"] }; | ||||||
|  |   // Verify the plugins module is called with the plugin options from shareable1.mjs and shareable2.mjs
 | ||||||
|  |   td.when( | ||||||
|  |     plugins( | ||||||
|  |       { options: expectedOptions, cwd }, | ||||||
|  |       { | ||||||
|  |         verifyRelease1: "./shareable1.mjs", | ||||||
|  |         verifyRelease2: "./shareable2.mjs", | ||||||
|  |         generateNotes2: "./shareable2.mjs", | ||||||
|  |         analyzeCommits1: "./shareable1.mjs", | ||||||
|  |         analyzeCommits2: "./shareable2.mjs", | ||||||
|  |       } | ||||||
|  |     ) | ||||||
|  |   ).thenResolve(pluginsConfig); | ||||||
|  | 
 | ||||||
|  |   const result = await t.context.getConfig({ cwd }); | ||||||
|  | 
 | ||||||
|  |   // Verify the options contains the plugin config from shareable1.json and shareable2.json
 | ||||||
|  |   t.deepEqual(result, { options: expectedOptions, plugins: pluginsConfig }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| test.serial('Prioritize configuration from config file over "extends"', async (t) => { | test.serial('Prioritize configuration from config file over "extends"', async (t) => { | ||||||
|   // Create a git repository, set the current working directory at the root of the repo
 |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|   const { cwd } = await gitRepo(); |   const { cwd } = await gitRepo(); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user