diff --git a/bin/sf-prepack.js b/bin/sf-prepack.js index 5d52fe1a..6db92905 100755 --- a/bin/sf-prepack.js +++ b/bin/sf-prepack.js @@ -23,11 +23,13 @@ if (isPlugin(packageRoot)) { console.log( chalk.yellow('Warning:'), // eslint-disable-next-line max-len - `oclif version ${version} is less than 3.14.0. Please upgrade to 3.14.0 or higher to use generate oclif.lock file.` + `oclif version ${version} is less than 3.14.0. Please upgrade to 3.14.0 or higher to generate oclif.lock file.` ); } else { shell.exec('oclif lock'); } + + shell.exec('npm shrinkwrap'); } else if (shell.which('oclif-dev')) { // eslint-disable-next-line no-console console.log(chalk.yellow('Warning:'), 'oclif-dev is deprecated. Please use oclif instead.'); diff --git a/files/gitignore b/files/gitignore index 633ca6b3..aaec877a 100644 --- a/files/gitignore +++ b/files/gitignore @@ -1,17 +1,20 @@ # -- CLEAN +tmp/ # use yarn by default, so ignore npm package-lock.json +npm-shrinkwrap.json # never checkin npm config .npmrc # debug logs -npm-error.log -yarn-error.log +*-error.log +*-debug.log # compile source lib +dist # test artifacts *xunit.xml @@ -19,10 +22,15 @@ lib *unitcoverage .nyc_output coverage +test_session* # generated docs docs +# oclif files +oclif.manifest.json +oclif.lock + # -- CLEAN ALL node_modules .wireit @@ -34,4 +42,4 @@ node_modules # os specific files .DS_Store -.idea \ No newline at end of file +.idea diff --git a/utils/sf-config.js b/utils/sf-config.js index 58791501..dd561690 100644 --- a/utils/sf-config.js +++ b/utils/sf-config.js @@ -98,6 +98,8 @@ const resolveConfig = (path) => { const pluginDefaults = { scripts: { ...PACKAGE_DEFAULTS.scripts, + 'clean:lib': undefined, + postpack: 'sf-clean', // wireit scripts don't need an entry in pjson scripts. // remove these from scripts and let wireit handle them (just repeat running yarn test) // https://github.com/google/wireit/blob/main/CHANGELOG.md#094---2023-01-30 diff --git a/utils/standardize-files.js b/utils/standardize-files.js index b7f7d105..0399acba 100644 --- a/utils/standardize-files.js +++ b/utils/standardize-files.js @@ -11,6 +11,7 @@ const log = require('./log'); const exists = require('./exists'); const { resolveConfig } = require('./sf-config'); const PackageJson = require('./package-json'); +const { isPlugin } = require('./project-type'); const FILES_PATH = join(__dirname, '..', 'files'); @@ -18,6 +19,19 @@ const FILE_NAME_LICENSE = 'LICENSE.txt'; const FILE_NAME_GITIGNORE = 'gitignore'; const FILE_NAME_MOCHARC = 'mocharc.json'; +// We don't copy over the .gitignore file since plugins/libraries might have their own +// unique additions to the file. So in order to programmatically add entries, we need to +// read the existing .gitignore file and append these entries to it in case they don't exist. +const IGNORES = [ + { pattern: 'node_modules', section: 'CLEAN ALL' }, + { pattern: '.eslintcache', section: 'CLEAN ALL' }, + { pattern: '.wireit', section: 'CLEAN ALL' }, + { pattern: '*.tsbuildinfo', section: 'CLEAN ALL' }, + { pattern: 'npm-shrinkwrap.json', section: 'CLEAN', plugin: true }, + { pattern: 'oclif.manifest.json', section: 'CLEAN', plugin: true }, + { pattern: 'oclif.lock', section: 'CLEAN', plugin: true }, +]; + function isDifferent(sourcePath, targetPath) { try { // Using .replace() to normalize line endings across operating systems. @@ -65,20 +79,68 @@ function writeGitignore(targetDir) { const copied = copyFile(gitignoreSourcePath, gitignoreTargetPath); if (!copied) { + const isAPlugin = isPlugin(targetDir); + const relevantPatterns = IGNORES.filter((entry) => !entry.plugin || (entry.plugin && isAPlugin)); let original = readFileSync(gitignoreTargetPath, 'utf-8'); - if (!original.includes('# -- CLEAN')) { + + const segments = original + // Segments are defined by "# --" in the gitignore + .split('# --') + // Turn each segment into list of valid gitignore lines + .map((segment) => segment.split('\n')) + // Maps segment name to list of valid gitignore lines + .reduce((map, segment) => { + const segmentName = (segment.shift() || '').trim(); + if (['CLEAN', 'CLEAN ALL'].includes(segmentName)) { + map[segmentName] = segment; + } + return map; + }, {}); + + let needsWrite = false; + if (Object.keys(segments).length === 0) { + // project doesn't have any #-- CLEAN or #-- CLEAN ALL segments + // add any missing entries to the end of the file log(`The .gitignore doesn't contain any clean entries. See ${gitignoreSourcePath} for examples.`); + + const toAdd = []; + for (const { pattern } of relevantPatterns) { + if (!original.includes(pattern)) { + needsWrite = true; + toAdd.push(pattern); + } + } + + if (needsWrite) { + writeFileSync(gitignoreTargetPath, `${original}\n${toAdd.join('\n')}`); + return gitignoreTargetPath; + } } else { - // Add the default clean-all entries if they don't exist. - let needsWrite = false; - for (const entry of ['.wireit', '.eslintcache', '*.tsbuildinfo']) { - if (!original.includes(entry)) { - original = original.replace('# -- CLEAN ALL', `# -- CLEAN ALL\n${entry}`); + // project has #-- CLEAN or #-- CLEAN ALL segments + // add any missing entries to the end of the segment + const updatedSegments = Object.fromEntries(Object.keys(segments).map((section) => [section, []])); + for (const { pattern, section } of relevantPatterns) { + if (!original.includes(pattern)) { needsWrite = true; + updatedSegments[section].push(pattern); + } else if (!segments[section].includes(pattern)) { + // the pattern is in the file, but not in the correct section + needsWrite = true; + original = original.replace(pattern, ''); + updatedSegments[section].push(pattern); } } + if (needsWrite) { - writeFileSync(gitignoreTargetPath, original); + for (const [section, lines] of Object.entries(updatedSegments)) { + if (lines.length === 0) continue; + original = original.replace( + `# -- ${section}\n${segments[section].join('\n')}`, + `# -- ${section}\n${[...segments[section], ...lines, '\n'].join('\n')}` + ); + } + writeFileSync(gitignoreTargetPath, original.trimEnd() + '\n'); + return gitignoreTargetPath; } } } diff --git a/utils/standardize-pjson.js b/utils/standardize-pjson.js index 35eeae04..8dc52ff8 100644 --- a/utils/standardize-pjson.js +++ b/utils/standardize-pjson.js @@ -13,6 +13,8 @@ const { semverIsLessThan } = require('./semver'); const PackageJson = require('./package-json'); const { isPlugin } = require('./project-type'); +const PLUGIN_FILES = ['/messages', '/oclif.manifest.json', '/oclif.lock', '/npm-shrinkwrap.json']; + module.exports = (packageRoot = require('./package-path')) => { const config = resolveConfig(packageRoot); const pjson = new PackageJson(packageRoot); @@ -25,6 +27,7 @@ module.exports = (packageRoot = require('./package-path')) => { if (isPlugin(packageRoot)) { pjson.contents.oclif.topicSeparator = ' '; + pjson.contents.files = [...new Set([...pjson.contents.files, ...PLUGIN_FILES])].sort(); } // GENERATE SCRIPTS const scriptList = Object.entries(config.scripts); diff --git a/utils/write-dependencies.js b/utils/write-dependencies.js index 1d4685db..9ca4c571 100644 --- a/utils/write-dependencies.js +++ b/utils/write-dependencies.js @@ -147,6 +147,15 @@ module.exports = (projectPath) => { } }); + // Check to see if these deps are used by yarn scripts. If not, remove them. + const possiblyUnnecessaryDeps = ['shx']; + + possiblyUnnecessaryDeps.forEach((dep) => { + if (!Object.values(scripts).some((script) => script?.includes(dep))) { + remove(dep); + } + }); + if (added.length > 0) { pjson.actions.push(`added/updated devDependencies ${added.join(', ')}`); }