/
githubmirror
/
yarn
Обзор
Документация
Войти
/
githubmirror
/
yarn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/cli/commands/pack.js
207 строк
6 KB
Kristoffer K
fix(pack): sort files to produce identical output on Windows and Unix (#8142)
21 май 2020, 20:27
Не верифицирован
21 май 2020, 20:27
17992a7
Код
Авторство
О чём код?
/* @flow */ import type {Reporter} from '../../reporters/index.js'; import type Config from '../../config.js'; import type {IgnoreFilter} from '../../util/filter.js'; import * as fs from '../../util/fs.js'; import {sortFilter, ignoreLinesToRegex, filterOverridenGitignores} from '../../util/filter.js'; import {MessageError} from '../../errors.js'; const zlib = require('zlib'); const path = require('path'); const tar = require('tar-fs'); const fs2 = require('fs'); const depsFor = require('hash-for-dep/lib/deps-for'); const FOLDERS_IGNORE = [ // never allow version control folders '.git', 'CVS', '.svn', '.hg', 'node_modules', ]; const DEFAULT_IGNORE = ignoreLinesToRegex([ ...FOLDERS_IGNORE, // ignore cruft 'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.yarnrc.yml', '.npmignore', '.gitignore', '.DS_Store', ]); const NEVER_IGNORE = ignoreLinesToRegex([ // never ignore these files '!/package.json', '!/readme*', '!/+(license|licence)*', '!/+(changes|changelog|history)*', ]); export async function packTarball( config: Config, {mapHeader}: {mapHeader?: Object => Object} = {}, ): Promise<stream$Duplex> { const pkg = await config.readRootManifest(); const {bundleDependencies, main, files: onlyFiles} = pkg; // include required files let filters: Array<IgnoreFilter> = NEVER_IGNORE.slice(); // include default filters unless `files` is used if (!onlyFiles) { filters = filters.concat(DEFAULT_IGNORE); } if (main) { filters = filters.concat(ignoreLinesToRegex(['!/' + main])); } // include bundleDependencies let bundleDependenciesFiles = []; if (bundleDependencies) { for (const dependency of bundleDependencies) { const dependencyList = depsFor(dependency, config.cwd); for (const dep of dependencyList) { const filesForBundledDep = await fs.walk(dep.baseDir, null, new Set(FOLDERS_IGNORE)); bundleDependenciesFiles = bundleDependenciesFiles.concat(filesForBundledDep); } } } // `files` field if (onlyFiles) { let lines = [ '*', // ignore all files except those that are explicitly included with a negation filter ]; lines = lines.concat( onlyFiles.map((filename: string): string => `!${filename}`), onlyFiles.map((filename: string): string => `!${path.join(filename, '**')}`), ); const regexes = ignoreLinesToRegex(lines, './'); filters = filters.concat(regexes); } const files = await fs.walk(config.cwd, null, new Set(FOLDERS_IGNORE)); const dotIgnoreFiles = filterOverridenGitignores(files); // create ignores for (const file of dotIgnoreFiles) { const raw = await fs.readFile(file.absolute); const lines = raw.split('\n'); const regexes = ignoreLinesToRegex(lines, path.dirname(file.relative)); filters = filters.concat(regexes); } // files to definitely keep, takes precedence over ignore filter const keepFiles: Set<string> = new Set(); // files to definitely ignore const ignoredFiles: Set<string> = new Set(); // list of files that didn't match any of our patterns, if a directory in the chain above was matched // then we should inherit it const possibleKeepFiles: Set<string> = new Set(); // apply filters sortFilter(files, filters, keepFiles, possibleKeepFiles, ignoredFiles); // add the files for the bundled dependencies to the set of files to keep for (const file of bundleDependenciesFiles) { const realPath = await fs.realpath(config.cwd); keepFiles.add(path.relative(realPath, file.absolute)); } return packWithIgnoreAndHeaders( config.cwd, name => { const relative = path.relative(config.cwd, name); // Don't ignore directories, since we need to recurse inside them to check for unignored files. if (fs2.lstatSync(name).isDirectory()) { const isParentOfKeptFile = Array.from(keepFiles).some(name => !path.relative(relative, name).startsWith('..')); return !isParentOfKeptFile; } // Otherwise, ignore a file if we're not supposed to keep it. return !keepFiles.has(relative); }, {mapHeader}, ); } export function packWithIgnoreAndHeaders( cwd: string, ignoreFunction?: string => boolean, {mapHeader}: {mapHeader?: Object => Object} = {}, ): Promise<stream$Duplex> { return tar.pack(cwd, { ignore: ignoreFunction, sort: true, map: header => { const suffix = header.name === '.' ? '' : `/${header.name}`; header.name = `package${suffix}`; delete header.uid; delete header.gid; return mapHeader ? mapHeader(header) : header; }, }); } export async function pack(config: Config): Promise<stream$Duplex> { const packer = await packTarball(config); const compressor = packer.pipe(new zlib.Gzip()); return compressor; } export function setFlags(commander: Object) { commander.description('Creates a compressed gzip archive of package dependencies.'); commander.option('-f, --filename <filename>', 'filename'); } export function hasWrapper(commander: Object, args: Array<string>): boolean { return true; } export async function run( config: Config, reporter: Reporter, flags: {filename?: string}, args?: Array<string>, ): Promise<void> { const pkg = await config.readRootManifest(); if (!pkg.name) { throw new MessageError(reporter.lang('noName')); } if (!pkg.version) { throw new MessageError(reporter.lang('noVersion')); } const normaliseScope = name => (name[0] === '@' ? name.substr(1).replace('/', '-') : name); const filename = flags.filename || path.join(config.cwd, `${normaliseScope(pkg.name)}-v${pkg.version}.tgz`); await config.executeLifecycleScript('prepack'); const stream = await pack(config); await new Promise((resolve, reject) => { stream.pipe(fs2.createWriteStream(filename)); stream.on('error', reject); stream.on('close', resolve); }); await config.executeLifecycleScript('postpack'); reporter.success(reporter.lang('packWroteTarball', filename)); }