diff --git a/README.md b/README.md
index 304f445f3..3726addd5 100644
--- a/README.md
+++ b/README.md
@@ -50,23 +50,23 @@ pkg [options]
Examples:
- – Makes executables for Linux, macOS and Windows
+ - Makes executables for Linux, macOS and Windows
$ pkg index.js
- – Takes package.json from cwd and follows 'bin' entry
+ - Takes package.json from cwd and follows 'bin' entry
$ pkg .
- – Makes executable for particular target machine
+ - Makes executable for particular target machine
$ pkg -t node16-win-arm64 index.js
- – Makes executables for target machines of your choice
+ - Makes executables for target machines of your choice
$ pkg -t node16-linux,node18-linux,node16-win index.js
- – Bakes '--expose-gc' and '--max-heap-size=34' into executable
+ - Bakes '--expose-gc' and '--max-heap-size=34' into executable
$ pkg --options "expose-gc,max-heap-size=34" index.js
- – Consider packageA and packageB to be public
+ - Consider packageA and packageB to be public
$ pkg --public-packages "packageA,packageB" index.js
- – Consider all packages to be public
+ - Consider all packages to be public
$ pkg --public-packages "*" index.js
- – Bakes '--expose-gc' into executable
+ - Bakes '--expose-gc' into executable
$ pkg --options expose-gc index.js
- – reduce size of the data packed inside the executable with GZip
+ - reduce size of the data packed inside the executable with GZip
$ pkg --compress GZip index.js
```
@@ -181,6 +181,22 @@ See also
[Detecting assets in source code](#detecting-assets-in-source-code) and
[Snapshot filesystem](#snapshot-filesystem).
+### Ignore files
+
+`ignore` is a list of globs. Files matching the paths specified as `ignore`
+will be excluded from the final executable.
+
+This is useful when you want to exclude some files from the final executable,
+like tests, documentation or build files that could have been included by a dependency.
+
+```json
+ "pkg": {
+ "ignore": [ "**/*/dependency-name/build.c" ]
+ }
+```
+
+To see if you have unwanted files in your executable, read the [Exploring virtual file system embedded in debug mode](#exploring-virtual-file-system-embedded-in-debug-mode) section.
+
### Options
Node.js application can be called with runtime options
@@ -413,7 +429,7 @@ printenv | grep NODE
## Advanced
-### exploring virtual file system embedded in debug mode
+### Exploring virtual file system embedded in debug mode
When you are using the `--debug` flag when building your executable,
`pkg` add the ability to display the content of the virtual file system
diff --git a/lib/index.ts b/lib/index.ts
index ea575ce97..dd7beb7af 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -27,6 +27,7 @@ import walk, { Marker, WalkerParams } from './walker';
import { Target, NodeTarget, SymLinks } from './types';
import { CompressType } from './compress_type';
import { patchMachOExecutable, signMachOExecutable } from './mach-o';
+import pkgOptions from './options';
const { version } = JSON.parse(
readFileSync(path.join(__dirname, '../package.json'), 'utf-8'),
@@ -598,12 +599,14 @@ export async function exec(argv2: string[]) {
let marker: Marker;
if (configJson) {
+ pkgOptions.set(configJson?.pkg);
marker = {
config: configJson,
base: path.dirname(config),
configPath: config,
};
} else {
+ pkgOptions.set(inputJson?.pkg);
marker = {
config: inputJson || {}, // not `inputBin` because only `input`
base: path.dirname(input), // is the place for `inputJson`
diff --git a/lib/options.ts b/lib/options.ts
new file mode 100644
index 000000000..6e34d13ec
--- /dev/null
+++ b/lib/options.ts
@@ -0,0 +1,23 @@
+import { PkgOptions } from './types';
+
+class Options {
+ private options: PkgOptions;
+
+ constructor() {
+ this.options = {
+ dictionary: {},
+ };
+ }
+
+ public set(options: PkgOptions): void {
+ this.options = options ?? this.options;
+ }
+
+ public get(): PkgOptions {
+ return this.options;
+ }
+}
+
+const options = new Options();
+
+export default options;
diff --git a/lib/types.ts b/lib/types.ts
index 89c41fd62..66039569a 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -36,6 +36,7 @@ export interface PkgOptions {
scripts?: string[];
log?: (logger: typeof log, context: Record) => void;
assets?: string[];
+ ignore?: string[];
deployFiles?: string[];
patches?: Patches;
dictionary: ConfigDictionary;
diff --git a/lib/walker.ts b/lib/walker.ts
index 418f5ca84..7daebf91d 100644
--- a/lib/walker.ts
+++ b/lib/walker.ts
@@ -6,6 +6,7 @@ import isCore from 'is-core-module';
import globby from 'globby';
import path from 'path';
import chalk from 'chalk';
+import { minimatch } from 'minimatch';
import {
ALIAS_AS_RELATIVE,
@@ -33,6 +34,7 @@ import {
PackageJson,
SymLinks,
} from './types';
+import pkgOptions from './options';
export interface Marker {
hasDictionary?: boolean;
@@ -450,6 +452,19 @@ class Walker {
assert(typeof task.file === 'string');
const realFile = toNormalizedRealPath(task.file);
+ const { ignore } = pkgOptions.get();
+ if (ignore) {
+ // check if the file matches one of the ignore regex patterns
+ const match = ignore.some((pattern) => minimatch(realFile, pattern));
+
+ if (match) {
+ log.debug(
+ `Ignoring file: ${realFile} due to top level config ignore pattern`,
+ );
+ return;
+ }
+ }
+
if (realFile === task.file) {
this.append(task);
return;
@@ -747,7 +762,11 @@ class Walker {
const catchPackageFilter = (config: PackageJson, base: string) => {
const newPackage = newPackages[newPackages.length - 1];
- newPackage.marker = { config, configPath: newPackage.packageJson, base };
+ newPackage.marker = {
+ config,
+ configPath: newPackage.packageJson,
+ base,
+ };
};
let newFile = '';
diff --git a/package.json b/package.json
index 76f42a453..00a7a5a2e 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"globby": "^11.1.0",
"into-stream": "^6.0.0",
"is-core-module": "2.9.0",
+ "minimatch": "9.0.4",
"minimist": "^1.2.6",
"multistream": "^4.1.0",
"prebuild-install": "7.1.1",
diff --git a/test/test-10-pnpm/main.js b/test/test-10-pnpm/main.js
index c1abca6a9..ae06890eb 100644
--- a/test/test-10-pnpm/main.js
+++ b/test/test-10-pnpm/main.js
@@ -13,6 +13,7 @@ if (utils.shouldSkipPnpm()) {
assert(__dirname === process.cwd());
+const isWindows = process.platform === 'win32';
const target = process.argv[2] || 'host';
const input = './test.js';
const output = './test-output.exe';
@@ -23,14 +24,14 @@ console.log('target = ', target);
utils.vacuum.sync('./node_modules');
utils.vacuum.sync('./pnpm-lock.yaml');
+const npmlog = utils.exec.sync('npm install -g pnpm@8');
+console.log('npm log :', npmlog);
+
// launch `pnpm install`
const pnpmlog = utils.spawn.sync(
- path.join(
- path.dirname(process.argv[0]),
- 'npx' + (process.platform === 'win32' ? '.cmd' : ''),
- ),
+ path.join(path.dirname(process.argv[0]), 'npx' + (isWindows ? '.cmd' : '')),
['pnpm', 'install'],
- { cwd: path.dirname(output), expect: 0 },
+ { cwd: path.dirname(output), expect: 0, shell: isWindows },
);
console.log('pnpm log :', pnpmlog);
diff --git a/test/test-11-pnpm/main.js b/test/test-11-pnpm/main.js
index d4821e00d..f708e6b42 100644
--- a/test/test-11-pnpm/main.js
+++ b/test/test-11-pnpm/main.js
@@ -14,6 +14,7 @@ if (utils.shouldSkipPnpm()) {
console.log(__dirname, process.cwd());
assert(__dirname === process.cwd());
+const isWindows = process.platform === 'win32';
const target = process.argv[2] || 'host';
const input = './test.js';
const output = './test-output.exe';
@@ -24,14 +25,14 @@ console.log('target = ', target);
utils.vacuum.sync('./node_modules');
utils.vacuum.sync('./pnpm-lock.yaml');
+const npmlog = utils.exec.sync('npm install -g pnpm@8');
+console.log('npm log :', npmlog);
+
// launch `pnpm install`
const pnpmlog = utils.spawn.sync(
- path.join(
- path.dirname(process.argv[0]),
- 'npx' + (process.platform === 'win32' ? '.cmd' : ''),
- ),
+ path.join(path.dirname(process.argv[0]), 'npx' + (isWindows ? '.cmd' : '')),
['pnpm', 'install'],
- { cwd: path.dirname(output), expect: 0 },
+ { cwd: path.dirname(output), expect: 0, shell: isWindows },
);
console.log('pnpm log :', pnpmlog);
diff --git a/test/test-50-ignore-files/.gitignore b/test/test-50-ignore-files/.gitignore
new file mode 100644
index 000000000..736e8ae58
--- /dev/null
+++ b/test/test-50-ignore-files/.gitignore
@@ -0,0 +1 @@
+!node_modules
\ No newline at end of file
diff --git a/test/test-50-ignore-files/main.js b/test/test-50-ignore-files/main.js
new file mode 100644
index 000000000..1db8e2592
--- /dev/null
+++ b/test/test-50-ignore-files/main.js
@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+
+'use strict';
+
+const path = require('path');
+const assert = require('assert');
+const utils = require('../utils.js');
+const standard = 'stdout';
+
+assert(!module.parent);
+assert(__dirname === process.cwd());
+
+const target = process.argv[2] || 'host';
+const output = './test-output.exe';
+
+let left, right;
+utils.mkdirp.sync(path.dirname(output));
+
+left = utils.spawn.sync('node', ['test-x-index.js']);
+
+const inspect =
+ standard === 'stdout'
+ ? ['inherit', 'pipe', 'inherit']
+ : ['inherit', 'inherit', 'pipe'];
+
+const log = utils.pkg.sync(
+ ['--target', target, '--output', output, '.', '--debug'],
+ inspect,
+);
+
+assert(
+ log.indexOf('useless.c due to top level config ignore pattern') > 0,
+ 'useless.c file is not ignored',
+);
+assert(
+ log.indexOf(
+ 'needed.c is added to queue',
+ 'needed.c file is not added to queue',
+ ),
+);
+
+right = utils.spawn.sync('./' + path.basename(output), [], {
+ cwd: path.dirname(output),
+});
+
+assert.strictEqual(left, right);
+utils.vacuum.sync(output);
diff --git a/test/test-50-ignore-files/node_modules/delta/index.js b/test/test-50-ignore-files/node_modules/delta/index.js
new file mode 100644
index 000000000..53a4bbf98
--- /dev/null
+++ b/test/test-50-ignore-files/node_modules/delta/index.js
@@ -0,0 +1,3 @@
+'use strict';
+
+global.FOO = 'bar';
diff --git a/test/test-50-ignore-files/node_modules/delta/needed.c b/test/test-50-ignore-files/node_modules/delta/needed.c
new file mode 100644
index 000000000..045d6c487
--- /dev/null
+++ b/test/test-50-ignore-files/node_modules/delta/needed.c
@@ -0,0 +1 @@
+// Needed file to include in bundle
\ No newline at end of file
diff --git a/test/test-50-ignore-files/node_modules/delta/package.json b/test/test-50-ignore-files/node_modules/delta/package.json
new file mode 100644
index 000000000..1c5ed3a50
--- /dev/null
+++ b/test/test-50-ignore-files/node_modules/delta/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "delta",
+ "main": "index.js",
+ "files": ["*.c"]
+}
diff --git a/test/test-50-ignore-files/node_modules/delta/useless.c b/test/test-50-ignore-files/node_modules/delta/useless.c
new file mode 100644
index 000000000..f527564a3
--- /dev/null
+++ b/test/test-50-ignore-files/node_modules/delta/useless.c
@@ -0,0 +1 @@
+// Some useless files
\ No newline at end of file
diff --git a/test/test-50-ignore-files/package.json b/test/test-50-ignore-files/package.json
new file mode 100644
index 000000000..88f7aa7ae
--- /dev/null
+++ b/test/test-50-ignore-files/package.json
@@ -0,0 +1,12 @@
+{
+ "bin": "test-x-index.js",
+ "license": "MIT",
+ "dependencies": {
+ "delta": "*"
+ },
+ "pkg": {
+ "ignore": [
+ "**/*/node_modules/delta/useless.c"
+ ]
+ }
+}
diff --git a/test/test-50-ignore-files/test-x-index.js b/test/test-50-ignore-files/test-x-index.js
new file mode 100644
index 000000000..259ec4f9b
--- /dev/null
+++ b/test/test-50-ignore-files/test-x-index.js
@@ -0,0 +1,5 @@
+'use strict';
+
+var dataPath = 'delta';
+require(dataPath);
+console.log(global.FOO);
diff --git a/test/test-80-compression-node-opcua/main.js b/test/test-80-compression-node-opcua/main.js
index c93558611..60a060f46 100644
--- a/test/test-80-compression-node-opcua/main.js
+++ b/test/test-80-compression-node-opcua/main.js
@@ -14,6 +14,7 @@ const assert = require('assert');
const utils = require('../utils.js');
const pkgJson = require('./package.json');
+const isWindows = process.platform === 'win32';
const buildDir = 'build';
assert(!module.parent);
@@ -32,14 +33,14 @@ function clean() {
// remove any possible left-over
clean();
+const npmlog = utils.exec.sync('npm install -g pnpm@8');
+console.log('npm log :', npmlog);
+
// launch `pnpm install`
const pnpmlog = utils.spawn.sync(
- path.join(
- path.dirname(process.argv[0]),
- 'npx' + (process.platform === 'win32' ? '.cmd' : ''),
- ),
+ path.join(path.dirname(process.argv[0]), 'npx' + (isWindows ? '.cmd' : '')),
['pnpm', 'install'],
- { cwd: path.dirname(__filename), expect: 0 },
+ { cwd: path.dirname(__filename), expect: 0, shell: isWindows },
);
console.log('pnpm log :', pnpmlog);
@@ -54,7 +55,7 @@ assert(
/* eslint-disable no-unused-vars */
const input = 'package.json';
const target = process.argv[2] || 'host';
-const ext = process.platform === 'win32' ? '.exe' : '';
+const ext = isWindows ? '.exe' : '';
const outputRef = path.join(buildDir, 'test-output-empty' + ext);
const outputNone = path.join(buildDir, 'test-output-None' + ext);
const outputGZip = path.join(buildDir, 'test-output-GZip' + ext);
@@ -95,10 +96,7 @@ function pkgCompress(compressMode, output) {
function esbuildBuild(entryPoint) {
const log = utils.spawn.sync(
- path.join(
- path.dirname(process.argv[0]),
- 'npx' + (process.platform === 'win32' ? '.cmd' : ''),
- ),
+ path.join(path.dirname(process.argv[0]), 'npx' + (isWindows ? '.cmd' : '')),
[
'esbuild',
entryPoint,
@@ -106,7 +104,7 @@ function esbuildBuild(entryPoint) {
'--outfile=' + path.join(buildDir, pkgJson.main),
'--platform=node',
],
- { cwd: __dirname, expect: 0 },
+ { cwd: __dirname, expect: 0, shell: isWindows },
);
console.log(log);
diff --git a/yarn.lock b/yarn.lock
index c76bae004..0b62d8f88 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -947,6 +947,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
@@ -3199,6 +3206,13 @@ mimic-response@^4.0.0:
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz"
integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==
+minimatch@9.0.4:
+ version "9.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51"
+ integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==
+ dependencies:
+ brace-expansion "^2.0.1"
+
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"