npm-pkgbuild
Version:
create ArchLinux, RPM and Debian packages from npm packages
298 lines (278 loc) • 7.63 kB
JavaScript
import { tmpdir, homedir } from "node:os";
import { join } from "node:path";
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { glob } from "node:fs/promises";
import Arborist from "@npmcli/arborist";
import { parse } from "ini";
import {
ContentEntry,
StringContentEntry,
CollectionEntry
} from "content-entry";
import { FileSystemEntry } from "content-entry-filesystem";
import { ContentProvider } from "./content-provider.mjs";
import { utf8StreamOptions } from "../util.mjs";
import { shrinkNPM } from "../npm-shrink.mjs";
/**
* Content from node_modules.
* Requires .npmrc or NPM_TOKEN environment
* @property {boolean} withoutDevelpmentDependencies
*/
export class NodeModulesContentProvider extends ContentProvider {
/**
* @return {string} name of the content provider
*/
static get name() {
return "node-modules";
}
static get description() {
return "use node_modules as source";
}
withoutDevelpmentDependencies = true;
constructor(definitions, entryProperties, directoryProperties) {
if (
entryProperties?.destination &&
!entryProperties.destination.endsWith("/")
) {
entryProperties.destination += "/";
}
super(definitions, entryProperties, directoryProperties);
Object.assign(this, definitions);
}
toString() {
return `${this.constructor.name}: ${this.dir} -> ${this.entryProperties.destination}`;
}
/**
* List all entries.
* @return {AsyncIterable<ContentEntry|CollectionEntry>} all entries
*/
async *[Symbol.asyncIterator]() {
try {
let pkgSourceDir = this.dir;
if (this.withoutDevelpmentDependencies) {
pkgSourceDir = await mkdtemp(join(tmpdir(), "modules"));
const json = JSON.parse(
//@ts-ignore
await readFile(join(this.dir, "package.json"), utf8StreamOptions)
);
delete json.devDependencies;
await writeFile(
join(pkgSourceDir, "package.json"),
JSON.stringify(json),
utf8StreamOptions
);
let npmrcContent;
const searchDirs = [pkgSourceDir, homedir()];
for (const d of searchDirs) {
try {
npmrcContent = await readFile(join(d, ".npmrc"), utf8StreamOptions);
break;
} catch {}
}
let npmrc = {};
if (npmrcContent) {
npmrc = parse(npmrcContent);
} else {
if (process.env.NPM_TOKEN) {
npmrc["_authToken"] = process.env.NPM_TOKEN;
} else {
throw new Error(
`.npmrc not found in ${searchDirs} (neither NPM_TOKEN in envinronment)`
);
}
}
const arb = new Arborist({ path: pkgSourceDir, ...npmrc });
await arb.buildIdealTree({
update: true,
prune: true,
saveType: "prod"
});
await arb.prune({ saveType: "prod" });
await arb.reify({ save: true });
}
const nodeModulesDir = join(pkgSourceDir, "node_modules");
const startPos = nodeModulesDir.length + 1;
for await (const entry of glob("**/*", {
cwd: nodeModulesDir,
withFileTypes: true
})) {
if (!toBeSkipped.test(entry.name)) {
const name = join(entry.parentPath, entry.name).substring(startPos);
if (entry.name.endsWith("package.json")) {
try {
const json = shrinkNPM(
JSON.parse(
//@ts-ignore
await readFile(
join(entry.parentPath, entry.name),
utf8StreamOptions
)
)
);
if (json) {
yield new StringContentEntry(
name,
this.entryProperties,
JSON.stringify(json)
);
}
} catch (e) {
console.error(e, entry.name);
}
}
if (entry.isFile()) {
yield new FileSystemEntry(name, {
...this.entryProperties,
baseDir: nodeModulesDir
});
} else if (entry.isDirectory()) {
yield new CollectionEntry(name, this.directoryProperties);
}
}
}
} catch (e) {
console.error(e);
throw e;
}
}
}
const toBeSkipped = new RegExp(
"(" +
[
"python3",
"\\.musl\\.node",
"package-lock.json",
"~",
"\\.\\d",
"\\.map",
"\\.umd\\.js",
"\\.ts",
"\\.mts",
"\\.d\\.ts",
"\\.d\\.cts",
"\\.debug\\.js",
"\\.coffee",
"tsdoc-metadata\\.json",
"\\.orig",
"\\.log",
"\\.tmp",
"\\.bak",
"\\.bat",
"\\.gypi",
"\\.gyp",
"\\.stamp",
"\\.cs",
"\\.cmd",
"\\.markdown",
"\\.circle.*",
"\\.lint.*",
"\\.yarn.*",
"\\.sublime-project",
"\\.sublime-workspace",
"appveyor\\.yml",
"seed\\.yml",
"\\.xclangspec",
"\\.pbfilespec",
"yarn\\.lock",
"\\.DS_Store",
"jenkinsfile",
"\\.vcxproj",
"\\.travis\\.yml",
"\\.jshint(rc)?",
"\\.npm.*",
"\\.git.*",
"\\.tar",
"\\.tgz",
"\\.tar\\.gz",
"rollup\\.config\\.(js|mjs|cjs)",
"COPYING",
"Copyrightnotice\\.txt",
"Doxyfile",
"Dockerfile",
"CODE_OF_CONDUCT(\\.md|\\.txt)?",
"GOVERNANCE(\\.md)?",
"CODEOWNERS(\\.md)?",
"UPGRAD(E|ING)(\\.md)?",
"AUTHORS(\\.md|\\.txt)?",
"OWNERS",
"CONTRIBUT(ORS|ING)(\\.md)?",
"CHANGELOG(\\.md)?",
"CHANGES(\\.md)?",
"HISTORY(\\.md)?",
"ROADMAP(\\.md)?",
"DOCUMENTATION(\\.md)?",
"LICEN[SC]E(\\-\\w+)?(\\.txt|\\.md|\\.BSD|\\.APACHE2|\\.MIT|\\.terms)?",
"README(.*\\.md|\\.txt)?",
"INSTALL(.*\\.md)?",
"\\.armv6\\.node",
"\\.py",
"\\.el",
"\\.o",
"\\.a",
"\\.c",
"\\.cc",
"\\.cpp",
"\\.h",
"\\.in",
"\\.vc",
"\\.m4",
"Makefile(\\.am|\\.fallback|\\.msc|\\.targ)?",
"\\.cmake",
"\\.mk",
"\\.patch",
"\\.esl*",
"\\.zuul\\.yml",
"\\.doclets\\.yml",
"\\.editorconfig",
"\\.tern-project",
"\\.dockerignore",
"\\.dir-locals\\.el",
"gulpfile\\.js",
"jsdoc\\.json",
"Gruntfile\\.js",
"karma\\.conf\\.js",
"verb\\.md",
"\\.nvmrc",
"bower\\.json",
"nodemon\\.json",
"\\.bash_completion.*",
"\\.coveralls\\.yml",
"\\.istanbul\\.yml",
"\\.babel.*",
"\\.nycrc",
"\\.env",
"x-package\\.json5?",
"component\\.json",
"tsconfig\\.json",
"cypress\\.json",
"\\.airtap\\.yml",
"\\.jscs\\.json",
"sauce-labs\\.svg",
"PATENTS(\\.md)?",
"release(-notes)?\\.md",
"NOTICE(\\.md)?",
"SUMMARY\\.md",
"MIGRAT.*\\.md",
"PULL_REQUEST_TEMPLATE\\.md",
"PATTERNS\\.md",
"REFERENCE\\.md",
"SECURITY\\.md",
"SFTPStream\\.md",
"LIMITS\\.md",
"[\\d\\-]+\\.md",
"DEPS",
"__tests__",
"\\.test\\.js",
"__snapshots__",
"Porting-Buffer\\.md",
"chains and topics\\.md",
"build_detect_platform",
"\\.snap",
"install_daemon_node.node",
"tsconfig.build.json",
"typedoc.json",
"webpack.config.js"
].join("|") +
")$|(node_modules/(npm-pkgbuild|@types|node-addon-api|mf-hosting|node-gyp$)|(win32|android|darwin)-(ia32|x64|arm|arm64))",
"i"
);