Change endpoint from persons to people

This commit is contained in:
xfarrow
2025-03-23 21:00:08 +01:00
parent 4ae263662c
commit d005193f63
7158 changed files with 700476 additions and 735 deletions

View File

@ -0,0 +1,51 @@
'use strict';
module.exports = exports = build;
exports.usage = 'Attempts to compile the module by dispatching to node-gyp or nw-gyp';
const napi = require('./util/napi.js');
const compile = require('./util/compile.js');
const handle_gyp_opts = require('./util/handle_gyp_opts.js');
const configure = require('./configure.js');
function do_build(gyp, argv, callback) {
handle_gyp_opts(gyp, argv, (err, result) => {
let final_args = ['build'].concat(result.gyp).concat(result.pre);
if (result.unparsed.length > 0) {
final_args = final_args.
concat(['--']).
concat(result.unparsed);
}
if (!err && result.opts.napi_build_version) {
napi.swap_build_dir_in(result.opts.napi_build_version);
}
compile.run_gyp(final_args, result.opts, (err2) => {
if (result.opts.napi_build_version) {
napi.swap_build_dir_out(result.opts.napi_build_version);
}
return callback(err2);
});
});
}
function build(gyp, argv, callback) {
// Form up commands to pass to node-gyp:
// We map `node-pre-gyp build` to `node-gyp configure build` so that we do not
// trigger a clean and therefore do not pay the penalty of a full recompile
if (argv.length && (argv.indexOf('rebuild') > -1)) {
argv.shift(); // remove `rebuild`
// here we map `node-pre-gyp rebuild` to `node-gyp rebuild` which internally means
// "clean + configure + build" and triggers a full recompile
compile.run_gyp(['clean'], {}, (err3) => {
if (err3) return callback(err3);
configure(gyp, argv, (err4) => {
if (err4) return callback(err4);
return do_build(gyp, argv, callback);
});
});
} else {
return do_build(gyp, argv, callback);
}
}

View File

@ -0,0 +1,31 @@
'use strict';
module.exports = exports = clean;
exports.usage = 'Removes the entire folder containing the compiled .node module';
const rm = require('rimraf');
const exists = require('fs').exists || require('path').exists;
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const path = require('path');
function clean(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
const to_delete = opts.module_path;
if (!to_delete) {
return callback(new Error('module_path is empty, refusing to delete'));
} else if (path.normalize(to_delete) === path.normalize(process.cwd())) {
return callback(new Error('module_path is not set, refusing to delete'));
} else {
exists(to_delete, (found) => {
if (found) {
if (!gyp.opts.silent_clean) console.log('[' + package_json.name + '] Removing "%s"', to_delete);
return rm(to_delete, callback);
}
return callback();
});
}
}

View File

@ -0,0 +1,52 @@
'use strict';
module.exports = exports = configure;
exports.usage = 'Attempts to configure node-gyp or nw-gyp build';
const napi = require('./util/napi.js');
const compile = require('./util/compile.js');
const handle_gyp_opts = require('./util/handle_gyp_opts.js');
function configure(gyp, argv, callback) {
handle_gyp_opts(gyp, argv, (err, result) => {
let final_args = result.gyp.concat(result.pre);
// pull select node-gyp configure options out of the npm environ
const known_gyp_args = ['dist-url', 'python', 'nodedir', 'msvs_version'];
known_gyp_args.forEach((key) => {
const val = gyp.opts[key] || gyp.opts[key.replace('-', '_')];
if (val) {
final_args.push('--' + key + '=' + val);
}
});
// --ensure=false tell node-gyp to re-install node development headers
// but it is only respected by node-gyp install, so we have to call install
// as a separate step if the user passes it
if (gyp.opts.ensure === false) {
const install_args = final_args.concat(['install', '--ensure=false']);
compile.run_gyp(install_args, result.opts, (err2) => {
if (err2) return callback(err2);
if (result.unparsed.length > 0) {
final_args = final_args.
concat(['--']).
concat(result.unparsed);
}
compile.run_gyp(['configure'].concat(final_args), result.opts, (err3) => {
return callback(err3);
});
});
} else {
if (result.unparsed.length > 0) {
final_args = final_args.
concat(['--']).
concat(result.unparsed);
}
compile.run_gyp(['configure'].concat(final_args), result.opts, (err4) => {
if (!err4 && result.opts.napi_build_version) {
napi.swap_build_dir_out(result.opts.napi_build_version);
}
return callback(err4);
});
}
});
}

View File

@ -0,0 +1,38 @@
'use strict';
module.exports = exports = info;
exports.usage = 'Lists all published binaries (requires aws-sdk)';
const log = require('npmlog');
const versioning = require('./util/versioning.js');
const s3_setup = require('./util/s3_setup.js');
function info(gyp, argv, callback) {
const package_json = gyp.package_json;
const opts = versioning.evaluate(package_json, gyp.opts);
const config = {};
s3_setup.detect(opts, config);
const s3 = s3_setup.get_s3(config);
const s3_opts = {
Bucket: config.bucket,
Prefix: config.prefix
};
s3.listObjects(s3_opts, (err, meta) => {
if (err && err.code === 'NotFound') {
return callback(new Error('[' + package_json.name + '] Not found: https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + config.prefix));
} else if (err) {
return callback(err);
} else {
log.verbose(JSON.stringify(meta, null, 1));
if (meta && meta.Contents) {
meta.Contents.forEach((obj) => {
console.log(obj.Key);
});
} else {
console.error('[' + package_json.name + '] No objects found at https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + config.prefix);
}
return callback();
}
});
}

View File

@ -0,0 +1,235 @@
'use strict';
module.exports = exports = install;
exports.usage = 'Attempts to install pre-built binary for module';
const fs = require('fs');
const path = require('path');
const log = require('npmlog');
const existsAsync = fs.exists || path.exists;
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const makeDir = require('make-dir');
// for fetching binaries
const fetch = require('node-fetch');
const tar = require('tar');
let npgVersion = 'unknown';
try {
// Read own package.json to get the current node-pre-pyp version.
const ownPackageJSON = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8');
npgVersion = JSON.parse(ownPackageJSON).version;
} catch (e) {
// do nothing
}
function place_binary(uri, targetDir, opts, callback) {
log.http('GET', uri);
// Try getting version info from the currently running npm.
const envVersionInfo = process.env.npm_config_user_agent ||
'node ' + process.version;
const sanitized = uri.replace('+', '%2B');
const requestOpts = {
uri: sanitized,
headers: {
'User-Agent': 'node-pre-gyp (v' + npgVersion + ', ' + envVersionInfo + ')'
},
follow_max: 10
};
if (opts.cafile) {
try {
requestOpts.ca = fs.readFileSync(opts.cafile);
} catch (e) {
return callback(e);
}
} else if (opts.ca) {
requestOpts.ca = opts.ca;
}
const proxyUrl = opts.proxy ||
process.env.http_proxy ||
process.env.HTTP_PROXY ||
process.env.npm_config_proxy;
let agent;
if (proxyUrl) {
const ProxyAgent = require('https-proxy-agent');
agent = new ProxyAgent(proxyUrl);
log.http('download', 'proxy agent configured using: "%s"', proxyUrl);
}
fetch(sanitized, { agent })
.then((res) => {
if (!res.ok) {
throw new Error(`response status ${res.status} ${res.statusText} on ${sanitized}`);
}
const dataStream = res.body;
return new Promise((resolve, reject) => {
let extractions = 0;
const countExtractions = (entry) => {
extractions += 1;
log.info('install', 'unpacking %s', entry.path);
};
dataStream.pipe(extract(targetDir, countExtractions))
.on('error', (e) => {
reject(e);
});
dataStream.on('end', () => {
resolve(`extracted file count: ${extractions}`);
});
dataStream.on('error', (e) => {
reject(e);
});
});
})
.then((text) => {
log.info(text);
callback();
})
.catch((e) => {
log.error(`install ${e.message}`);
callback(e);
});
}
function extract(to, onentry) {
return tar.extract({
cwd: to,
strip: 1,
onentry
});
}
function extract_from_local(from, targetDir, callback) {
if (!fs.existsSync(from)) {
return callback(new Error('Cannot find file ' + from));
}
log.info('Found local file to extract from ' + from);
// extract helpers
let extractCount = 0;
function countExtractions(entry) {
extractCount += 1;
log.info('install', 'unpacking ' + entry.path);
}
function afterExtract(err) {
if (err) return callback(err);
if (extractCount === 0) {
return callback(new Error('There was a fatal problem while extracting the tarball'));
}
log.info('tarball', 'done parsing tarball');
callback();
}
fs.createReadStream(from).pipe(extract(targetDir, countExtractions))
.on('close', afterExtract)
.on('error', afterExtract);
}
function do_build(gyp, argv, callback) {
const args = ['rebuild'].concat(argv);
gyp.todo.push({ name: 'build', args: args });
process.nextTick(callback);
}
function print_fallback_error(err, opts, package_json) {
const fallback_message = ' (falling back to source compile with node-gyp)';
let full_message = '';
if (err.statusCode !== undefined) {
// If we got a network response it but failed to download
// it means remote binaries are not available, so let's try to help
// the user/developer with the info to debug why
full_message = 'Pre-built binaries not found for ' + package_json.name + '@' + package_json.version;
full_message += ' and ' + opts.runtime + '@' + (opts.target || process.versions.node) + ' (' + opts.node_abi + ' ABI, ' + opts.libc + ')';
full_message += fallback_message;
log.warn('Tried to download(' + err.statusCode + '): ' + opts.hosted_tarball);
log.warn(full_message);
log.http(err.message);
} else {
// If we do not have a statusCode that means an unexpected error
// happened and prevented an http response, so we output the exact error
full_message = 'Pre-built binaries not installable for ' + package_json.name + '@' + package_json.version;
full_message += ' and ' + opts.runtime + '@' + (opts.target || process.versions.node) + ' (' + opts.node_abi + ' ABI, ' + opts.libc + ')';
full_message += fallback_message;
log.warn(full_message);
log.warn('Hit error ' + err.message);
}
}
//
// install
//
function install(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const source_build = gyp.opts['build-from-source'] || gyp.opts.build_from_source;
const update_binary = gyp.opts['update-binary'] || gyp.opts.update_binary;
const should_do_source_build = source_build === package_json.name || (source_build === true || source_build === 'true');
if (should_do_source_build) {
log.info('build', 'requesting source compile');
return do_build(gyp, argv, callback);
} else {
const fallback_to_build = gyp.opts['fallback-to-build'] || gyp.opts.fallback_to_build;
let should_do_fallback_build = fallback_to_build === package_json.name || (fallback_to_build === true || fallback_to_build === 'true');
// but allow override from npm
if (process.env.npm_config_argv) {
const cooked = JSON.parse(process.env.npm_config_argv).cooked;
const match = cooked.indexOf('--fallback-to-build');
if (match > -1 && cooked.length > match && cooked[match + 1] === 'false') {
should_do_fallback_build = false;
log.info('install', 'Build fallback disabled via npm flag: --fallback-to-build=false');
}
}
let opts;
try {
opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
} catch (err) {
return callback(err);
}
opts.ca = gyp.opts.ca;
opts.cafile = gyp.opts.cafile;
const from = opts.hosted_tarball;
const to = opts.module_path;
const binary_module = path.join(to, opts.module_name + '.node');
existsAsync(binary_module, (found) => {
if (!update_binary) {
if (found) {
console.log('[' + package_json.name + '] Success: "' + binary_module + '" already installed');
console.log('Pass --update-binary to reinstall or --build-from-source to recompile');
return callback();
}
log.info('check', 'checked for "' + binary_module + '" (not found)');
}
makeDir(to).then(() => {
const fileName = from.startsWith('file://') && from.slice('file://'.length);
if (fileName) {
extract_from_local(fileName, to, after_place);
} else {
place_binary(from, to, opts, after_place);
}
}).catch((err) => {
after_place(err);
});
function after_place(err) {
if (err && should_do_fallback_build) {
print_fallback_error(err, opts, package_json);
return do_build(gyp, argv, callback);
} else if (err) {
return callback(err);
} else {
console.log('[' + package_json.name + '] Success: "' + binary_module + '" is installed via remote');
return callback();
}
}
});
}
}

View File

@ -0,0 +1,125 @@
'use strict';
/**
* Set the title.
*/
process.title = 'node-pre-gyp';
const node_pre_gyp = require('../');
const log = require('npmlog');
/**
* Process and execute the selected commands.
*/
const prog = new node_pre_gyp.Run({ argv: process.argv });
let completed = false;
if (prog.todo.length === 0) {
if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) {
console.log('v%s', prog.version);
process.exit(0);
} else if (~process.argv.indexOf('-h') || ~process.argv.indexOf('--help')) {
console.log('%s', prog.usage());
process.exit(0);
}
console.log('%s', prog.usage());
process.exit(1);
}
// if --no-color is passed
if (prog.opts && Object.hasOwnProperty.call(prog, 'color') && !prog.opts.color) {
log.disableColor();
}
log.info('it worked if it ends with', 'ok');
log.verbose('cli', process.argv);
log.info('using', process.title + '@%s', prog.version);
log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch);
/**
* Change dir if -C/--directory was passed.
*/
const dir = prog.opts.directory;
if (dir) {
const fs = require('fs');
try {
const stat = fs.statSync(dir);
if (stat.isDirectory()) {
log.info('chdir', dir);
process.chdir(dir);
} else {
log.warn('chdir', dir + ' is not a directory');
}
} catch (e) {
if (e.code === 'ENOENT') {
log.warn('chdir', dir + ' is not a directory');
} else {
log.warn('chdir', 'error during chdir() "%s"', e.message);
}
}
}
function run() {
const command = prog.todo.shift();
if (!command) {
// done!
completed = true;
log.info('ok');
return;
}
// set binary.host when appropriate. host determines the s3 target bucket.
const target = prog.setBinaryHostProperty(command.name);
if (target && ['install', 'publish', 'unpublish', 'info'].indexOf(command.name) >= 0) {
log.info('using binary.host: ' + prog.package_json.binary.host);
}
prog.commands[command.name](command.args, function(err) {
if (err) {
log.error(command.name + ' error');
log.error('stack', err.stack);
errorMessage();
log.error('not ok');
console.log(err.message);
return process.exit(1);
}
const args_array = [].slice.call(arguments, 1);
if (args_array.length) {
console.log.apply(console, args_array);
}
// now run the next command in the queue
process.nextTick(run);
});
}
process.on('exit', (code) => {
if (!completed && !code) {
log.error('Completion callback never invoked!');
errorMessage();
process.exit(6);
}
});
process.on('uncaughtException', (err) => {
log.error('UNCAUGHT EXCEPTION');
log.error('stack', err.stack);
errorMessage();
process.exit(7);
});
function errorMessage() {
// copied from npm's lib/util/error-handler.js
const os = require('os');
log.error('System', os.type() + ' ' + os.release());
log.error('command', process.argv.map(JSON.stringify).join(' '));
log.error('cwd', process.cwd());
log.error('node -v', process.version);
log.error(process.title + ' -v', 'v' + prog.package.version);
}
// start running the given commands!
run();

View File

@ -0,0 +1,309 @@
'use strict';
/**
* Module exports.
*/
module.exports = exports;
/**
* Module dependencies.
*/
// load mocking control function for accessing s3 via https. the function is a noop always returning
// false if not mocking.
exports.mockS3Http = require('./util/s3_setup').get_mockS3Http();
exports.mockS3Http('on');
const mocking = exports.mockS3Http('get');
const fs = require('fs');
const path = require('path');
const nopt = require('nopt');
const log = require('npmlog');
log.disableProgress();
const napi = require('./util/napi.js');
const EE = require('events').EventEmitter;
const inherits = require('util').inherits;
const cli_commands = [
'clean',
'install',
'reinstall',
'build',
'rebuild',
'package',
'testpackage',
'publish',
'unpublish',
'info',
'testbinary',
'reveal',
'configure'
];
const aliases = {};
// differentiate node-pre-gyp's logs from npm's
log.heading = 'node-pre-gyp';
if (mocking) {
log.warn(`mocking s3 to ${process.env.node_pre_gyp_mock_s3}`);
}
// this is a getter to avoid circular reference warnings with node v14.
Object.defineProperty(exports, 'find', {
get: function() {
return require('./pre-binding').find;
},
enumerable: true
});
// in the following, "my_module" is using node-pre-gyp to
// prebuild and install pre-built binaries. "main_module"
// is using "my_module".
//
// "bin/node-pre-gyp" invokes Run() without a path. the
// expectation is that the working directory is the package
// root "my_module". this is true because in all cases npm is
// executing a script in the context of "my_module".
//
// "pre-binding.find()" is executed by "my_module" but in the
// context of "main_module". this is because "main_module" is
// executing and requires "my_module" which is then executing
// "pre-binding.find()" via "node-pre-gyp.find()", so the working
// directory is that of "main_module".
//
// that's why "find()" must pass the path to package.json.
//
function Run({ package_json_path = './package.json', argv }) {
this.package_json_path = package_json_path;
this.commands = {};
const self = this;
cli_commands.forEach((command) => {
self.commands[command] = function(argvx, callback) {
log.verbose('command', command, argvx);
return require('./' + command)(self, argvx, callback);
};
});
this.parseArgv(argv);
// this is set to true after the binary.host property was set to
// either staging_host or production_host.
this.binaryHostSet = false;
}
inherits(Run, EE);
exports.Run = Run;
const proto = Run.prototype;
/**
* Export the contents of the package.json.
*/
proto.package = require('../package.json');
/**
* nopt configuration definitions
*/
proto.configDefs = {
help: Boolean, // everywhere
arch: String, // 'configure'
debug: Boolean, // 'build'
directory: String, // bin
proxy: String, // 'install'
loglevel: String // everywhere
};
/**
* nopt shorthands
*/
proto.shorthands = {
release: '--no-debug',
C: '--directory',
debug: '--debug',
j: '--jobs',
silent: '--loglevel=silent',
silly: '--loglevel=silly',
verbose: '--loglevel=verbose'
};
/**
* expose the command aliases for the bin file to use.
*/
proto.aliases = aliases;
/**
* Parses the given argv array and sets the 'opts', 'argv',
* 'command', and 'package_json' properties.
*/
proto.parseArgv = function parseOpts(argv) {
this.opts = nopt(this.configDefs, this.shorthands, argv);
this.argv = this.opts.argv.remain.slice();
const commands = this.todo = [];
// create a copy of the argv array with aliases mapped
argv = this.argv.map((arg) => {
// is this an alias?
if (arg in this.aliases) {
arg = this.aliases[arg];
}
return arg;
});
// process the mapped args into "command" objects ("name" and "args" props)
argv.slice().forEach((arg) => {
if (arg in this.commands) {
const args = argv.splice(0, argv.indexOf(arg));
argv.shift();
if (commands.length > 0) {
commands[commands.length - 1].args = args;
}
commands.push({ name: arg, args: [] });
}
});
if (commands.length > 0) {
commands[commands.length - 1].args = argv.splice(0);
}
// if a directory was specified package.json is assumed to be relative
// to it.
let package_json_path = this.package_json_path;
if (this.opts.directory) {
package_json_path = path.join(this.opts.directory, package_json_path);
}
this.package_json = JSON.parse(fs.readFileSync(package_json_path));
// expand commands entries for multiple napi builds
this.todo = napi.expand_commands(this.package_json, this.opts, commands);
// support for inheriting config env variables from npm
const npm_config_prefix = 'npm_config_';
Object.keys(process.env).forEach((name) => {
if (name.indexOf(npm_config_prefix) !== 0) return;
const val = process.env[name];
if (name === npm_config_prefix + 'loglevel') {
log.level = val;
} else {
// add the user-defined options to the config
name = name.substring(npm_config_prefix.length);
// avoid npm argv clobber already present args
// which avoids problem of 'npm test' calling
// script that runs unique npm install commands
if (name === 'argv') {
if (this.opts.argv &&
this.opts.argv.remain &&
this.opts.argv.remain.length) {
// do nothing
} else {
this.opts[name] = val;
}
} else {
this.opts[name] = val;
}
}
});
if (this.opts.loglevel) {
log.level = this.opts.loglevel;
}
log.resume();
};
/**
* allow the binary.host property to be set at execution time.
*
* for this to take effect requires all the following to be true.
* - binary is a property in package.json
* - binary.host is falsey
* - binary.staging_host is not empty
* - binary.production_host is not empty
*
* if any of the previous checks fail then the function returns an empty string
* and makes no changes to package.json's binary property.
*
*
* if command is "publish" then the default is set to "binary.staging_host"
* if command is not "publish" the the default is set to "binary.production_host"
*
* if the command-line option '--s3_host' is set to "staging" or "production" then
* "binary.host" is set to the specified "staging_host" or "production_host". if
* '--s3_host' is any other value an exception is thrown.
*
* if '--s3_host' is not present then "binary.host" is set to the default as above.
*
* this strategy was chosen so that any command other than "publish" or "unpublish" uses "production"
* as the default without requiring any command-line options but that "publish" and "unpublish" require
* '--s3_host production_host' to be specified in order to *really* publish (or unpublish). publishing
* to staging can be done freely without worrying about disturbing any production releases.
*/
proto.setBinaryHostProperty = function(command) {
if (this.binaryHostSet) {
return this.package_json.binary.host;
}
const p = this.package_json;
// don't set anything if host is present. it must be left blank to trigger this.
if (!p || !p.binary || p.binary.host) {
return '';
}
// and both staging and production must be present. errors will be reported later.
if (!p.binary.staging_host || !p.binary.production_host) {
return '';
}
let target = 'production_host';
if (command === 'publish' || command === 'unpublish') {
target = 'staging_host';
}
// the environment variable has priority over the default or the command line. if
// either the env var or the command line option are invalid throw an error.
const npg_s3_host = process.env.node_pre_gyp_s3_host;
if (npg_s3_host === 'staging' || npg_s3_host === 'production') {
target = `${npg_s3_host}_host`;
} else if (this.opts['s3_host'] === 'staging' || this.opts['s3_host'] === 'production') {
target = `${this.opts['s3_host']}_host`;
} else if (this.opts['s3_host'] || npg_s3_host) {
throw new Error(`invalid s3_host ${this.opts['s3_host'] || npg_s3_host}`);
}
p.binary.host = p.binary[target];
this.binaryHostSet = true;
return p.binary.host;
};
/**
* Returns the usage instructions for node-pre-gyp.
*/
proto.usage = function usage() {
const str = [
'',
' Usage: node-pre-gyp <command> [options]',
'',
' where <command> is one of:',
cli_commands.map((c) => {
return ' - ' + c + ' - ' + require('./' + c).usage;
}).join('\n'),
'',
'node-pre-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'),
'node@' + process.versions.node
].join('\n');
return str;
};
/**
* Version number getter.
*/
Object.defineProperty(proto, 'version', {
get: function() {
return this.package.version;
},
enumerable: true
});

View File

@ -0,0 +1,73 @@
'use strict';
module.exports = exports = _package;
exports.usage = 'Packs binary (and enclosing directory) into locally staged tarball';
const fs = require('fs');
const path = require('path');
const log = require('npmlog');
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const existsAsync = fs.exists || path.exists;
const makeDir = require('make-dir');
const tar = require('tar');
function readdirSync(dir) {
let list = [];
const files = fs.readdirSync(dir);
files.forEach((file) => {
const stats = fs.lstatSync(path.join(dir, file));
if (stats.isDirectory()) {
list = list.concat(readdirSync(path.join(dir, file)));
} else {
list.push(path.join(dir, file));
}
});
return list;
}
function _package(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
const from = opts.module_path;
const binary_module = path.join(from, opts.module_name + '.node');
existsAsync(binary_module, (found) => {
if (!found) {
return callback(new Error('Cannot package because ' + binary_module + ' missing: run `node-pre-gyp rebuild` first'));
}
const tarball = opts.staged_tarball;
const filter_func = function(entry) {
const basename = path.basename(entry);
if (basename.length && basename[0] !== '.') {
console.log('packing ' + entry);
return true;
} else {
console.log('skipping ' + entry);
}
return false;
};
makeDir(path.dirname(tarball)).then(() => {
let files = readdirSync(from);
const base = path.basename(from);
files = files.map((file) => {
return path.join(base, path.relative(from, file));
});
tar.create({
portable: false,
gzip: true,
filter: filter_func,
file: tarball,
cwd: path.dirname(from)
}, files, (err2) => {
if (err2) console.error('[' + package_json.name + '] ' + err2.message);
else log.info('package', 'Binary staged at "' + tarball + '"');
return callback(err2);
});
}).catch((err) => {
return callback(err);
});
});
}

View File

@ -0,0 +1,34 @@
'use strict';
const npg = require('..');
const versioning = require('../lib/util/versioning.js');
const napi = require('../lib/util/napi.js');
const existsSync = require('fs').existsSync || require('path').existsSync;
const path = require('path');
module.exports = exports;
exports.usage = 'Finds the require path for the node-pre-gyp installed module';
exports.validate = function(package_json, opts) {
versioning.validate_config(package_json, opts);
};
exports.find = function(package_json_path, opts) {
if (!existsSync(package_json_path)) {
throw new Error(package_json_path + 'does not exist');
}
const prog = new npg.Run({ package_json_path, argv: process.argv });
prog.setBinaryHostProperty();
const package_json = prog.package_json;
versioning.validate_config(package_json, opts);
let napi_build_version;
if (napi.get_napi_build_versions(package_json, opts)) {
napi_build_version = napi.get_best_napi_build_version(package_json, opts);
}
opts = opts || {};
if (!opts.module_root) opts.module_root = path.dirname(package_json_path);
const meta = versioning.evaluate(package_json, opts, napi_build_version);
return meta.module;
};

View File

@ -0,0 +1,81 @@
'use strict';
module.exports = exports = publish;
exports.usage = 'Publishes pre-built binary (requires aws-sdk)';
const fs = require('fs');
const path = require('path');
const log = require('npmlog');
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const s3_setup = require('./util/s3_setup.js');
const existsAsync = fs.exists || path.exists;
const url = require('url');
function publish(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
const tarball = opts.staged_tarball;
existsAsync(tarball, (found) => {
if (!found) {
return callback(new Error('Cannot publish because ' + tarball + ' missing: run `node-pre-gyp package` first'));
}
log.info('publish', 'Detecting s3 credentials');
const config = {};
s3_setup.detect(opts, config);
const s3 = s3_setup.get_s3(config);
const key_name = url.resolve(config.prefix, opts.package_name);
const s3_opts = {
Bucket: config.bucket,
Key: key_name
};
log.info('publish', 'Authenticating with s3');
log.info('publish', config);
log.info('publish', 'Checking for existing binary at ' + opts.hosted_path);
s3.headObject(s3_opts, (err, meta) => {
if (meta) log.info('publish', JSON.stringify(meta));
if (err && err.code === 'NotFound') {
// we are safe to publish because
// the object does not already exist
log.info('publish', 'Preparing to put object');
const s3_put_opts = {
ACL: 'public-read',
Body: fs.createReadStream(tarball),
Key: key_name,
Bucket: config.bucket
};
log.info('publish', 'Putting object', s3_put_opts.ACL, s3_put_opts.Bucket, s3_put_opts.Key);
try {
s3.putObject(s3_put_opts, (err2, resp) => {
log.info('publish', 'returned from putting object');
if (err2) {
log.info('publish', 's3 putObject error: "' + err2 + '"');
return callback(err2);
}
if (resp) log.info('publish', 's3 putObject response: "' + JSON.stringify(resp) + '"');
log.info('publish', 'successfully put object');
console.log('[' + package_json.name + '] published to ' + opts.hosted_path);
return callback();
});
} catch (err3) {
log.info('publish', 's3 putObject error: "' + err3 + '"');
return callback(err3);
}
} else if (err) {
log.info('publish', 's3 headObject error: "' + err + '"');
return callback(err);
} else {
log.error('publish', 'Cannot publish over existing version');
log.error('publish', "Update the 'version' field in package.json and try again");
log.error('publish', 'If the previous version was published in error see:');
log.error('publish', '\t node-pre-gyp unpublish');
return callback(new Error('Failed publishing to ' + opts.hosted_path));
}
});
});
}

View File

@ -0,0 +1,20 @@
'use strict';
module.exports = exports = rebuild;
exports.usage = 'Runs "clean" and "build" at once';
const napi = require('./util/napi.js');
function rebuild(gyp, argv, callback) {
const package_json = gyp.package_json;
let commands = [
{ name: 'clean', args: [] },
{ name: 'build', args: ['rebuild'] }
];
commands = napi.expand_commands(package_json, gyp.opts, commands);
for (let i = commands.length; i !== 0; i--) {
gyp.todo.unshift(commands[i - 1]);
}
process.nextTick(callback);
}

View File

@ -0,0 +1,19 @@
'use strict';
module.exports = exports = rebuild;
exports.usage = 'Runs "clean" and "install" at once';
const napi = require('./util/napi.js');
function rebuild(gyp, argv, callback) {
const package_json = gyp.package_json;
let installArgs = [];
const napi_build_version = napi.get_best_napi_build_version(package_json, gyp.opts);
if (napi_build_version != null) installArgs = [napi.get_command_arg(napi_build_version)];
gyp.todo.unshift(
{ name: 'clean', args: [] },
{ name: 'install', args: installArgs }
);
process.nextTick(callback);
}

View File

@ -0,0 +1,32 @@
'use strict';
module.exports = exports = reveal;
exports.usage = 'Reveals data on the versioned binary';
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
function unix_paths(key, val) {
return val && val.replace ? val.replace(/\\/g, '/') : val;
}
function reveal(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
let hit = false;
// if a second arg is passed look to see
// if it is a known option
// console.log(JSON.stringify(gyp.opts,null,1))
const remain = gyp.opts.argv.remain[gyp.opts.argv.remain.length - 1];
if (remain && Object.hasOwnProperty.call(opts, remain)) {
console.log(opts[remain].replace(/\\/g, '/'));
hit = true;
}
// otherwise return all options as json
if (!hit) {
console.log(JSON.stringify(opts, unix_paths, 2));
}
return callback();
}

View File

@ -0,0 +1,79 @@
'use strict';
module.exports = exports = testbinary;
exports.usage = 'Tests that the binary.node can be required';
const path = require('path');
const log = require('npmlog');
const cp = require('child_process');
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
function testbinary(gyp, argv, callback) {
const args = [];
const options = {};
let shell_cmd = process.execPath;
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
// skip validation for runtimes we don't explicitly support (like electron)
if (opts.runtime &&
opts.runtime !== 'node-webkit' &&
opts.runtime !== 'node') {
return callback();
}
const nw = (opts.runtime && opts.runtime === 'node-webkit');
// ensure on windows that / are used for require path
const binary_module = opts.module.replace(/\\/g, '/');
if ((process.arch !== opts.target_arch) ||
(process.platform !== opts.target_platform)) {
let msg = 'skipping validation since host platform/arch (';
msg += process.platform + '/' + process.arch + ')';
msg += ' does not match target (';
msg += opts.target_platform + '/' + opts.target_arch + ')';
log.info('validate', msg);
return callback();
}
if (nw) {
options.timeout = 5000;
if (process.platform === 'darwin') {
shell_cmd = 'node-webkit';
} else if (process.platform === 'win32') {
shell_cmd = 'nw.exe';
} else {
shell_cmd = 'nw';
}
const modulePath = path.resolve(binary_module);
const appDir = path.join(__dirname, 'util', 'nw-pre-gyp');
args.push(appDir);
args.push(modulePath);
log.info('validate', "Running test command: '" + shell_cmd + ' ' + args.join(' ') + "'");
cp.execFile(shell_cmd, args, options, (err, stdout, stderr) => {
// check for normal timeout for node-webkit
if (err) {
if (err.killed === true && err.signal && err.signal.indexOf('SIG') > -1) {
return callback();
}
const stderrLog = stderr.toString();
log.info('stderr', stderrLog);
if (/^\s*Xlib:\s*extension\s*"RANDR"\s*missing\s*on\s*display\s*":\d+\.\d+"\.\s*$/.test(stderrLog)) {
log.info('RANDR', 'stderr contains only RANDR error, ignored');
return callback();
}
return callback(err);
}
return callback();
});
return;
}
args.push('--eval');
args.push("require('" + binary_module.replace(/'/g, '\'') + "')");
log.info('validate', "Running test command: '" + shell_cmd + ' ' + args.join(' ') + "'");
cp.execFile(shell_cmd, args, options, (err, stdout, stderr) => {
if (err) {
return callback(err, { stdout: stdout, stderr: stderr });
}
return callback();
});
}

View File

@ -0,0 +1,53 @@
'use strict';
module.exports = exports = testpackage;
exports.usage = 'Tests that the staged package is valid';
const fs = require('fs');
const path = require('path');
const log = require('npmlog');
const existsAsync = fs.exists || path.exists;
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const testbinary = require('./testbinary.js');
const tar = require('tar');
const makeDir = require('make-dir');
function testpackage(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
const tarball = opts.staged_tarball;
existsAsync(tarball, (found) => {
if (!found) {
return callback(new Error('Cannot test package because ' + tarball + ' missing: run `node-pre-gyp package` first'));
}
const to = opts.module_path;
function filter_func(entry) {
log.info('install', 'unpacking [' + entry.path + ']');
}
makeDir(to).then(() => {
tar.extract({
file: tarball,
cwd: to,
strip: 1,
onentry: filter_func
}).then(after_extract, callback);
}).catch((err) => {
return callback(err);
});
function after_extract() {
testbinary(gyp, argv, (err) => {
if (err) {
return callback(err);
} else {
console.log('[' + package_json.name + '] Package appears valid');
return callback();
}
});
}
});
}

View File

@ -0,0 +1,41 @@
'use strict';
module.exports = exports = unpublish;
exports.usage = 'Unpublishes pre-built binary (requires aws-sdk)';
const log = require('npmlog');
const versioning = require('./util/versioning.js');
const napi = require('./util/napi.js');
const s3_setup = require('./util/s3_setup.js');
const url = require('url');
function unpublish(gyp, argv, callback) {
const package_json = gyp.package_json;
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
const config = {};
s3_setup.detect(opts, config);
const s3 = s3_setup.get_s3(config);
const key_name = url.resolve(config.prefix, opts.package_name);
const s3_opts = {
Bucket: config.bucket,
Key: key_name
};
s3.headObject(s3_opts, (err, meta) => {
if (err && err.code === 'NotFound') {
console.log('[' + package_json.name + '] Not found: https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + s3_opts.Key);
return callback();
} else if (err) {
return callback(err);
} else {
log.info('unpublish', JSON.stringify(meta));
s3.deleteObject(s3_opts, (err2, resp) => {
if (err2) return callback(err2);
log.info(JSON.stringify(resp));
console.log('[' + package_json.name + '] Success: removed https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + s3_opts.Key);
return callback();
});
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
'use strict';
module.exports = exports;
const fs = require('fs');
const path = require('path');
const win = process.platform === 'win32';
const existsSync = fs.existsSync || path.existsSync;
const cp = require('child_process');
// try to build up the complete path to node-gyp
/* priority:
- node-gyp on ENV:npm_config_node_gyp (https://github.com/npm/npm/pull/4887)
- node-gyp on NODE_PATH
- node-gyp inside npm on NODE_PATH (ignore on iojs)
- node-gyp inside npm beside node exe
*/
function which_node_gyp() {
let node_gyp_bin;
if (process.env.npm_config_node_gyp) {
try {
node_gyp_bin = process.env.npm_config_node_gyp;
if (existsSync(node_gyp_bin)) {
return node_gyp_bin;
}
} catch (err) {
// do nothing
}
}
try {
const node_gyp_main = require.resolve('node-gyp'); // eslint-disable-line node/no-missing-require
node_gyp_bin = path.join(path.dirname(
path.dirname(node_gyp_main)),
'bin/node-gyp.js');
if (existsSync(node_gyp_bin)) {
return node_gyp_bin;
}
} catch (err) {
// do nothing
}
if (process.execPath.indexOf('iojs') === -1) {
try {
const npm_main = require.resolve('npm'); // eslint-disable-line node/no-missing-require
node_gyp_bin = path.join(path.dirname(
path.dirname(npm_main)),
'node_modules/node-gyp/bin/node-gyp.js');
if (existsSync(node_gyp_bin)) {
return node_gyp_bin;
}
} catch (err) {
// do nothing
}
}
const npm_base = path.join(path.dirname(
path.dirname(process.execPath)),
'lib/node_modules/npm/');
node_gyp_bin = path.join(npm_base, 'node_modules/node-gyp/bin/node-gyp.js');
if (existsSync(node_gyp_bin)) {
return node_gyp_bin;
}
}
module.exports.run_gyp = function(args, opts, callback) {
let shell_cmd = '';
const cmd_args = [];
if (opts.runtime && opts.runtime === 'node-webkit') {
shell_cmd = 'nw-gyp';
if (win) shell_cmd += '.cmd';
} else {
const node_gyp_path = which_node_gyp();
if (node_gyp_path) {
shell_cmd = process.execPath;
cmd_args.push(node_gyp_path);
} else {
shell_cmd = 'node-gyp';
if (win) shell_cmd += '.cmd';
}
}
const final_args = cmd_args.concat(args);
const cmd = cp.spawn(shell_cmd, final_args, { cwd: undefined, env: process.env, stdio: [0, 1, 2] });
cmd.on('error', (err) => {
if (err) {
return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + err + ')'));
}
callback(null, opts);
});
cmd.on('close', (code) => {
if (code && code !== 0) {
return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + code + ')'));
}
callback(null, opts);
});
};

View File

@ -0,0 +1,102 @@
'use strict';
module.exports = exports = handle_gyp_opts;
const versioning = require('./versioning.js');
const napi = require('./napi.js');
/*
Here we gather node-pre-gyp generated options (from versioning) and pass them along to node-gyp.
We massage the args and options slightly to account for differences in what commands mean between
node-pre-gyp and node-gyp (e.g. see the difference between "build" and "rebuild" below)
Keep in mind: the values inside `argv` and `gyp.opts` below are different depending on whether
node-pre-gyp is called directory, or if it is called in a `run-script` phase of npm.
We also try to preserve any command line options that might have been passed to npm or node-pre-gyp.
But this is fairly difficult without passing way to much through. For example `gyp.opts` contains all
the process.env and npm pushes a lot of variables into process.env which node-pre-gyp inherits. So we have
to be very selective about what we pass through.
For example:
`npm install --build-from-source` will give:
argv == [ 'rebuild' ]
gyp.opts.argv == { remain: [ 'install' ],
cooked: [ 'install', '--fallback-to-build' ],
original: [ 'install', '--fallback-to-build' ] }
`./bin/node-pre-gyp build` will give:
argv == []
gyp.opts.argv == { remain: [ 'build' ],
cooked: [ 'build' ],
original: [ '-C', 'test/app1', 'build' ] }
*/
// select set of node-pre-gyp versioning info
// to share with node-gyp
const share_with_node_gyp = [
'module',
'module_name',
'module_path',
'napi_version',
'node_abi_napi',
'napi_build_version',
'node_napi_label'
];
function handle_gyp_opts(gyp, argv, callback) {
// Collect node-pre-gyp specific variables to pass to node-gyp
const node_pre_gyp_options = [];
// generate custom node-pre-gyp versioning info
const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
const opts = versioning.evaluate(gyp.package_json, gyp.opts, napi_build_version);
share_with_node_gyp.forEach((key) => {
const val = opts[key];
if (val) {
node_pre_gyp_options.push('--' + key + '=' + val);
} else if (key === 'napi_build_version') {
node_pre_gyp_options.push('--' + key + '=0');
} else {
if (key !== 'napi_version' && key !== 'node_abi_napi')
return callback(new Error('Option ' + key + ' required but not found by node-pre-gyp'));
}
});
// Collect options that follow the special -- which disables nopt parsing
const unparsed_options = [];
let double_hyphen_found = false;
gyp.opts.argv.original.forEach((opt) => {
if (double_hyphen_found) {
unparsed_options.push(opt);
}
if (opt === '--') {
double_hyphen_found = true;
}
});
// We try respect and pass through remaining command
// line options (like --foo=bar) to node-gyp
const cooked = gyp.opts.argv.cooked;
const node_gyp_options = [];
cooked.forEach((value) => {
if (value.length > 2 && value.slice(0, 2) === '--') {
const key = value.slice(2);
const val = cooked[cooked.indexOf(value) + 1];
if (val && val.indexOf('--') === -1) { // handle '--foo=bar' or ['--foo','bar']
node_gyp_options.push('--' + key + '=' + val);
} else { // pass through --foo
node_gyp_options.push(value);
}
}
});
const result = { 'opts': opts, 'gyp': node_gyp_options, 'pre': node_pre_gyp_options, 'unparsed': unparsed_options };
return callback(null, result);
}

View File

@ -0,0 +1,205 @@
'use strict';
const fs = require('fs');
module.exports = exports;
const versionArray = process.version
.substr(1)
.replace(/-.*$/, '')
.split('.')
.map((item) => {
return +item;
});
const napi_multiple_commands = [
'build',
'clean',
'configure',
'package',
'publish',
'reveal',
'testbinary',
'testpackage',
'unpublish'
];
const napi_build_version_tag = 'napi_build_version=';
module.exports.get_napi_version = function() {
// returns the non-zero numeric napi version or undefined if napi is not supported.
// correctly supporting target requires an updated cross-walk
let version = process.versions.napi; // can be undefined
if (!version) { // this code should never need to be updated
if (versionArray[0] === 9 && versionArray[1] >= 3) version = 2; // 9.3.0+
else if (versionArray[0] === 8) version = 1; // 8.0.0+
}
return version;
};
module.exports.get_napi_version_as_string = function(target) {
// returns the napi version as a string or an empty string if napi is not supported.
const version = module.exports.get_napi_version(target);
return version ? '' + version : '';
};
module.exports.validate_package_json = function(package_json, opts) { // throws Error
const binary = package_json.binary;
const module_path_ok = pathOK(binary.module_path);
const remote_path_ok = pathOK(binary.remote_path);
const package_name_ok = pathOK(binary.package_name);
const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts, true);
const napi_build_versions_raw = module.exports.get_napi_build_versions_raw(package_json);
if (napi_build_versions) {
napi_build_versions.forEach((napi_build_version)=> {
if (!(parseInt(napi_build_version, 10) === napi_build_version && napi_build_version > 0)) {
throw new Error('All values specified in napi_versions must be positive integers.');
}
});
}
if (napi_build_versions && (!module_path_ok || (!remote_path_ok && !package_name_ok))) {
throw new Error('When napi_versions is specified; module_path and either remote_path or ' +
"package_name must contain the substitution string '{napi_build_version}`.");
}
if ((module_path_ok || remote_path_ok || package_name_ok) && !napi_build_versions_raw) {
throw new Error("When the substitution string '{napi_build_version}` is specified in " +
'module_path, remote_path, or package_name; napi_versions must also be specified.');
}
if (napi_build_versions && !module.exports.get_best_napi_build_version(package_json, opts) &&
module.exports.build_napi_only(package_json)) {
throw new Error(
'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
'This Node instance cannot run this module.');
}
if (napi_build_versions_raw && !napi_build_versions && module.exports.build_napi_only(package_json)) {
throw new Error(
'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
'This Node instance cannot run this module.');
}
};
function pathOK(path) {
return path && (path.indexOf('{napi_build_version}') !== -1 || path.indexOf('{node_napi_label}') !== -1);
}
module.exports.expand_commands = function(package_json, opts, commands) {
const expanded_commands = [];
const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
commands.forEach((command)=> {
if (napi_build_versions && command.name === 'install') {
const napi_build_version = module.exports.get_best_napi_build_version(package_json, opts);
const args = napi_build_version ? [napi_build_version_tag + napi_build_version] : [];
expanded_commands.push({ name: command.name, args: args });
} else if (napi_build_versions && napi_multiple_commands.indexOf(command.name) !== -1) {
napi_build_versions.forEach((napi_build_version)=> {
const args = command.args.slice();
args.push(napi_build_version_tag + napi_build_version);
expanded_commands.push({ name: command.name, args: args });
});
} else {
expanded_commands.push(command);
}
});
return expanded_commands;
};
module.exports.get_napi_build_versions = function(package_json, opts, warnings) { // opts may be undefined
const log = require('npmlog');
let napi_build_versions = [];
const supported_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
// remove duplicates, verify each napi version can actaully be built
if (package_json.binary && package_json.binary.napi_versions) {
package_json.binary.napi_versions.forEach((napi_version) => {
const duplicated = napi_build_versions.indexOf(napi_version) !== -1;
if (!duplicated && supported_napi_version && napi_version <= supported_napi_version) {
napi_build_versions.push(napi_version);
} else if (warnings && !duplicated && supported_napi_version) {
log.info('This Node instance does not support builds for Node-API version', napi_version);
}
});
}
if (opts && opts['build-latest-napi-version-only']) {
let latest_version = 0;
napi_build_versions.forEach((napi_version) => {
if (napi_version > latest_version) latest_version = napi_version;
});
napi_build_versions = latest_version ? [latest_version] : [];
}
return napi_build_versions.length ? napi_build_versions : undefined;
};
module.exports.get_napi_build_versions_raw = function(package_json) {
const napi_build_versions = [];
// remove duplicates
if (package_json.binary && package_json.binary.napi_versions) {
package_json.binary.napi_versions.forEach((napi_version) => {
if (napi_build_versions.indexOf(napi_version) === -1) {
napi_build_versions.push(napi_version);
}
});
}
return napi_build_versions.length ? napi_build_versions : undefined;
};
module.exports.get_command_arg = function(napi_build_version) {
return napi_build_version_tag + napi_build_version;
};
module.exports.get_napi_build_version_from_command_args = function(command_args) {
for (let i = 0; i < command_args.length; i++) {
const arg = command_args[i];
if (arg.indexOf(napi_build_version_tag) === 0) {
return parseInt(arg.substr(napi_build_version_tag.length), 10);
}
}
return undefined;
};
module.exports.swap_build_dir_out = function(napi_build_version) {
if (napi_build_version) {
const rm = require('rimraf');
rm.sync(module.exports.get_build_dir(napi_build_version));
fs.renameSync('build', module.exports.get_build_dir(napi_build_version));
}
};
module.exports.swap_build_dir_in = function(napi_build_version) {
if (napi_build_version) {
const rm = require('rimraf');
rm.sync('build');
fs.renameSync(module.exports.get_build_dir(napi_build_version), 'build');
}
};
module.exports.get_build_dir = function(napi_build_version) {
return 'build-tmp-napi-v' + napi_build_version;
};
module.exports.get_best_napi_build_version = function(package_json, opts) {
let best_napi_build_version = 0;
const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
if (napi_build_versions) {
const our_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
napi_build_versions.forEach((napi_build_version)=> {
if (napi_build_version > best_napi_build_version &&
napi_build_version <= our_napi_version) {
best_napi_build_version = napi_build_version;
}
});
}
return best_napi_build_version === 0 ? undefined : best_napi_build_version;
};
module.exports.build_napi_only = function(package_json) {
return package_json.binary && package_json.binary.package_name &&
package_json.binary.package_name.indexOf('{node_napi_label}') === -1;
};

View File

@ -0,0 +1,26 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Node-webkit-based module test</title>
<script>
function nwModuleTest(){
var util = require('util');
var moduleFolder = require('nw.gui').App.argv[0];
try {
require(moduleFolder);
} catch(e) {
if( process.platform !== 'win32' ){
util.log('nw-pre-gyp error:');
util.log(e.stack);
}
process.exit(1);
}
process.exit(0);
}
</script>
</head>
<body onload="nwModuleTest()">
<h1>Node-webkit-based module test</h1>
</body>
</html>

View File

@ -0,0 +1,9 @@
{
"main": "index.html",
"name": "nw-pre-gyp-module-test",
"description": "Node-webkit-based module test.",
"version": "0.0.1",
"window": {
"show": false
}
}

View File

@ -0,0 +1,163 @@
'use strict';
module.exports = exports;
const url = require('url');
const fs = require('fs');
const path = require('path');
module.exports.detect = function(opts, config) {
const to = opts.hosted_path;
const uri = url.parse(to);
config.prefix = (!uri.pathname || uri.pathname === '/') ? '' : uri.pathname.replace('/', '');
if (opts.bucket && opts.region) {
config.bucket = opts.bucket;
config.region = opts.region;
config.endpoint = opts.host;
config.s3ForcePathStyle = opts.s3ForcePathStyle;
} else {
const parts = uri.hostname.split('.s3');
const bucket = parts[0];
if (!bucket) {
return;
}
if (!config.bucket) {
config.bucket = bucket;
}
if (!config.region) {
const region = parts[1].slice(1).split('.')[0];
if (region === 'amazonaws') {
config.region = 'us-east-1';
} else {
config.region = region;
}
}
}
};
module.exports.get_s3 = function(config) {
if (process.env.node_pre_gyp_mock_s3) {
// here we're mocking. node_pre_gyp_mock_s3 is the scratch directory
// for the mock code.
const AWSMock = require('mock-aws-s3');
const os = require('os');
AWSMock.config.basePath = `${os.tmpdir()}/mock`;
const s3 = AWSMock.S3();
// wrapped callback maker. fs calls return code of ENOENT but AWS.S3 returns
// NotFound.
const wcb = (fn) => (err, ...args) => {
if (err && err.code === 'ENOENT') {
err.code = 'NotFound';
}
return fn(err, ...args);
};
return {
listObjects(params, callback) {
return s3.listObjects(params, wcb(callback));
},
headObject(params, callback) {
return s3.headObject(params, wcb(callback));
},
deleteObject(params, callback) {
return s3.deleteObject(params, wcb(callback));
},
putObject(params, callback) {
return s3.putObject(params, wcb(callback));
}
};
}
// if not mocking then setup real s3.
const AWS = require('aws-sdk');
AWS.config.update(config);
const s3 = new AWS.S3();
// need to change if additional options need to be specified.
return {
listObjects(params, callback) {
return s3.listObjects(params, callback);
},
headObject(params, callback) {
return s3.headObject(params, callback);
},
deleteObject(params, callback) {
return s3.deleteObject(params, callback);
},
putObject(params, callback) {
return s3.putObject(params, callback);
}
};
};
//
// function to get the mocking control function. if not mocking it returns a no-op.
//
// if mocking it sets up the mock http interceptors that use the mocked s3 file system
// to fulfill reponses.
module.exports.get_mockS3Http = function() {
let mock_s3 = false;
if (!process.env.node_pre_gyp_mock_s3) {
return () => mock_s3;
}
const nock = require('nock');
// the bucket used for testing, as addressed by https.
const host = 'https://mapbox-node-pre-gyp-public-testing-bucket.s3.us-east-1.amazonaws.com';
const mockDir = process.env.node_pre_gyp_mock_s3 + '/mapbox-node-pre-gyp-public-testing-bucket';
// function to setup interceptors. they are "turned off" by setting mock_s3 to false.
const mock_http = () => {
// eslint-disable-next-line no-unused-vars
function get(uri, requestBody) {
const filepath = path.join(mockDir, uri.replace('%2B', '+'));
try {
fs.accessSync(filepath, fs.constants.R_OK);
} catch (e) {
return [404, 'not found\n'];
}
// the mock s3 functions just write to disk, so just read from it.
return [200, fs.createReadStream(filepath)];
}
// eslint-disable-next-line no-unused-vars
return nock(host)
.persist()
.get(() => mock_s3) // mock any uri for s3 when true
.reply(get);
};
// setup interceptors. they check the mock_s3 flag to determine whether to intercept.
mock_http(nock, host, mockDir);
// function to turn matching all requests to s3 on/off.
const mockS3Http = (action) => {
const previous = mock_s3;
if (action === 'off') {
mock_s3 = false;
} else if (action === 'on') {
mock_s3 = true;
} else if (action !== 'get') {
throw new Error(`illegal action for setMockHttp ${action}`);
}
return previous;
};
// call mockS3Http with the argument
// - 'on' - turn it on
// - 'off' - turn it off (used by fetch.test.js so it doesn't interfere with redirects)
// - 'get' - return true or false for 'on' or 'off'
return mockS3Http;
};

View File

@ -0,0 +1,335 @@
'use strict';
module.exports = exports;
const path = require('path');
const semver = require('semver');
const url = require('url');
const detect_libc = require('detect-libc');
const napi = require('./napi.js');
let abi_crosswalk;
// This is used for unit testing to provide a fake
// ABI crosswalk that emulates one that is not updated
// for the current version
if (process.env.NODE_PRE_GYP_ABI_CROSSWALK) {
abi_crosswalk = require(process.env.NODE_PRE_GYP_ABI_CROSSWALK);
} else {
abi_crosswalk = require('./abi_crosswalk.json');
}
const major_versions = {};
Object.keys(abi_crosswalk).forEach((v) => {
const major = v.split('.')[0];
if (!major_versions[major]) {
major_versions[major] = v;
}
});
function get_electron_abi(runtime, target_version) {
if (!runtime) {
throw new Error('get_electron_abi requires valid runtime arg');
}
if (typeof target_version === 'undefined') {
// erroneous CLI call
throw new Error('Empty target version is not supported if electron is the target.');
}
// Electron guarantees that patch version update won't break native modules.
const sem_ver = semver.parse(target_version);
return runtime + '-v' + sem_ver.major + '.' + sem_ver.minor;
}
module.exports.get_electron_abi = get_electron_abi;
function get_node_webkit_abi(runtime, target_version) {
if (!runtime) {
throw new Error('get_node_webkit_abi requires valid runtime arg');
}
if (typeof target_version === 'undefined') {
// erroneous CLI call
throw new Error('Empty target version is not supported if node-webkit is the target.');
}
return runtime + '-v' + target_version;
}
module.exports.get_node_webkit_abi = get_node_webkit_abi;
function get_node_abi(runtime, versions) {
if (!runtime) {
throw new Error('get_node_abi requires valid runtime arg');
}
if (!versions) {
throw new Error('get_node_abi requires valid process.versions object');
}
const sem_ver = semver.parse(versions.node);
if (sem_ver.major === 0 && sem_ver.minor % 2) { // odd series
// https://github.com/mapbox/node-pre-gyp/issues/124
return runtime + '-v' + versions.node;
} else {
// process.versions.modules added in >= v0.10.4 and v0.11.7
// https://github.com/joyent/node/commit/ccabd4a6fa8a6eb79d29bc3bbe9fe2b6531c2d8e
return versions.modules ? runtime + '-v' + (+versions.modules) :
'v8-' + versions.v8.split('.').slice(0, 2).join('.');
}
}
module.exports.get_node_abi = get_node_abi;
function get_runtime_abi(runtime, target_version) {
if (!runtime) {
throw new Error('get_runtime_abi requires valid runtime arg');
}
if (runtime === 'node-webkit') {
return get_node_webkit_abi(runtime, target_version || process.versions['node-webkit']);
} else if (runtime === 'electron') {
return get_electron_abi(runtime, target_version || process.versions.electron);
} else {
if (runtime !== 'node') {
throw new Error("Unknown Runtime: '" + runtime + "'");
}
if (!target_version) {
return get_node_abi(runtime, process.versions);
} else {
let cross_obj;
// abi_crosswalk generated with ./scripts/abi_crosswalk.js
if (abi_crosswalk[target_version]) {
cross_obj = abi_crosswalk[target_version];
} else {
const target_parts = target_version.split('.').map((i) => { return +i; });
if (target_parts.length !== 3) { // parse failed
throw new Error('Unknown target version: ' + target_version);
}
/*
The below code tries to infer the last known ABI compatible version
that we have recorded in the abi_crosswalk.json when an exact match
is not possible. The reasons for this to exist are complicated:
- We support passing --target to be able to allow developers to package binaries for versions of node
that are not the same one as they are running. This might also be used in combination with the
--target_arch or --target_platform flags to also package binaries for alternative platforms
- When --target is passed we can't therefore determine the ABI (process.versions.modules) from the node
version that is running in memory
- So, therefore node-pre-gyp keeps an "ABI crosswalk" (lib/util/abi_crosswalk.json) to be able to look
this info up for all versions
- But we cannot easily predict what the future ABI will be for released versions
- And node-pre-gyp needs to be a `bundledDependency` in apps that depend on it in order to work correctly
by being fully available at install time.
- So, the speed of node releases and the bundled nature of node-pre-gyp mean that a new node-pre-gyp release
need to happen for every node.js/io.js/node-webkit/nw.js/atom-shell/etc release that might come online if
you want the `--target` flag to keep working for the latest version
- Which is impractical ^^
- Hence the below code guesses about future ABI to make the need to update node-pre-gyp less demanding.
In practice then you can have a dependency of your app like `node-sqlite3` that bundles a `node-pre-gyp` that
only knows about node v0.10.33 in the `abi_crosswalk.json` but target node v0.10.34 (which is assumed to be
ABI compatible with v0.10.33).
TODO: use semver module instead of custom version parsing
*/
const major = target_parts[0];
let minor = target_parts[1];
let patch = target_parts[2];
// io.js: yeah if node.js ever releases 1.x this will break
// but that is unlikely to happen: https://github.com/iojs/io.js/pull/253#issuecomment-69432616
if (major === 1) {
// look for last release that is the same major version
// e.g. we assume io.js 1.x is ABI compatible with >= 1.0.0
while (true) {
if (minor > 0) --minor;
if (patch > 0) --patch;
const new_iojs_target = '' + major + '.' + minor + '.' + patch;
if (abi_crosswalk[new_iojs_target]) {
cross_obj = abi_crosswalk[new_iojs_target];
console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
console.log('Warning: but node-pre-gyp successfully choose ' + new_iojs_target + ' as ABI compatible target');
break;
}
if (minor === 0 && patch === 0) {
break;
}
}
} else if (major >= 2) {
// look for last release that is the same major version
if (major_versions[major]) {
cross_obj = abi_crosswalk[major_versions[major]];
console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
console.log('Warning: but node-pre-gyp successfully choose ' + major_versions[major] + ' as ABI compatible target');
}
} else if (major === 0) { // node.js
if (target_parts[1] % 2 === 0) { // for stable/even node.js series
// look for the last release that is the same minor release
// e.g. we assume node 0.10.x is ABI compatible with >= 0.10.0
while (--patch > 0) {
const new_node_target = '' + major + '.' + minor + '.' + patch;
if (abi_crosswalk[new_node_target]) {
cross_obj = abi_crosswalk[new_node_target];
console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
console.log('Warning: but node-pre-gyp successfully choose ' + new_node_target + ' as ABI compatible target');
break;
}
}
}
}
}
if (!cross_obj) {
throw new Error('Unsupported target version: ' + target_version);
}
// emulate process.versions
const versions_obj = {
node: target_version,
v8: cross_obj.v8 + '.0',
// abi_crosswalk uses 1 for node versions lacking process.versions.modules
// process.versions.modules added in >= v0.10.4 and v0.11.7
modules: cross_obj.node_abi > 1 ? cross_obj.node_abi : undefined
};
return get_node_abi(runtime, versions_obj);
}
}
}
module.exports.get_runtime_abi = get_runtime_abi;
const required_parameters = [
'module_name',
'module_path',
'host'
];
function validate_config(package_json, opts) {
const msg = package_json.name + ' package.json is not node-pre-gyp ready:\n';
const missing = [];
if (!package_json.main) {
missing.push('main');
}
if (!package_json.version) {
missing.push('version');
}
if (!package_json.name) {
missing.push('name');
}
if (!package_json.binary) {
missing.push('binary');
}
const o = package_json.binary;
if (o) {
required_parameters.forEach((p) => {
if (!o[p] || typeof o[p] !== 'string') {
missing.push('binary.' + p);
}
});
}
if (missing.length >= 1) {
throw new Error(msg + 'package.json must declare these properties: \n' + missing.join('\n'));
}
if (o) {
// enforce https over http
const protocol = url.parse(o.host).protocol;
if (protocol === 'http:') {
throw new Error("'host' protocol (" + protocol + ") is invalid - only 'https:' is accepted");
}
}
napi.validate_package_json(package_json, opts);
}
module.exports.validate_config = validate_config;
function eval_template(template, opts) {
Object.keys(opts).forEach((key) => {
const pattern = '{' + key + '}';
while (template.indexOf(pattern) > -1) {
template = template.replace(pattern, opts[key]);
}
});
return template;
}
// url.resolve needs single trailing slash
// to behave correctly, otherwise a double slash
// may end up in the url which breaks requests
// and a lacking slash may not lead to proper joining
function fix_slashes(pathname) {
if (pathname.slice(-1) !== '/') {
return pathname + '/';
}
return pathname;
}
// remove double slashes
// note: path.normalize will not work because
// it will convert forward to back slashes
function drop_double_slashes(pathname) {
return pathname.replace(/\/\//g, '/');
}
function get_process_runtime(versions) {
let runtime = 'node';
if (versions['node-webkit']) {
runtime = 'node-webkit';
} else if (versions.electron) {
runtime = 'electron';
}
return runtime;
}
module.exports.get_process_runtime = get_process_runtime;
const default_package_name = '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
const default_remote_path = '';
module.exports.evaluate = function(package_json, options, napi_build_version) {
options = options || {};
validate_config(package_json, options); // options is a suitable substitute for opts in this case
const v = package_json.version;
const module_version = semver.parse(v);
const runtime = options.runtime || get_process_runtime(process.versions);
const opts = {
name: package_json.name,
configuration: options.debug ? 'Debug' : 'Release',
debug: options.debug,
module_name: package_json.binary.module_name,
version: module_version.version,
prerelease: module_version.prerelease.length ? module_version.prerelease.join('.') : '',
build: module_version.build.length ? module_version.build.join('.') : '',
major: module_version.major,
minor: module_version.minor,
patch: module_version.patch,
runtime: runtime,
node_abi: get_runtime_abi(runtime, options.target),
node_abi_napi: napi.get_napi_version(options.target) ? 'napi' : get_runtime_abi(runtime, options.target),
napi_version: napi.get_napi_version(options.target), // non-zero numeric, undefined if unsupported
napi_build_version: napi_build_version || '',
node_napi_label: napi_build_version ? 'napi-v' + napi_build_version : get_runtime_abi(runtime, options.target),
target: options.target || '',
platform: options.target_platform || process.platform,
target_platform: options.target_platform || process.platform,
arch: options.target_arch || process.arch,
target_arch: options.target_arch || process.arch,
libc: options.target_libc || detect_libc.familySync() || 'unknown',
module_main: package_json.main,
toolset: options.toolset || '', // address https://github.com/mapbox/node-pre-gyp/issues/119
bucket: package_json.binary.bucket,
region: package_json.binary.region,
s3ForcePathStyle: package_json.binary.s3ForcePathStyle || false
};
// support host mirror with npm config `--{module_name}_binary_host_mirror`
// e.g.: https://github.com/node-inspector/v8-profiler/blob/master/package.json#L25
// > npm install v8-profiler --profiler_binary_host_mirror=https://npm.taobao.org/mirrors/node-inspector/
const validModuleName = opts.module_name.replace('-', '_');
const host = process.env['npm_config_' + validModuleName + '_binary_host_mirror'] || package_json.binary.host;
opts.host = fix_slashes(eval_template(host, opts));
opts.module_path = eval_template(package_json.binary.module_path, opts);
// now we resolve the module_path to ensure it is absolute so that binding.gyp variables work predictably
if (options.module_root) {
// resolve relative to known module root: works for pre-binding require
opts.module_path = path.join(options.module_root, opts.module_path);
} else {
// resolve relative to current working directory: works for node-pre-gyp commands
opts.module_path = path.resolve(opts.module_path);
}
opts.module = path.join(opts.module_path, opts.module_name + '.node');
opts.remote_path = package_json.binary.remote_path ? drop_double_slashes(fix_slashes(eval_template(package_json.binary.remote_path, opts))) : default_remote_path;
const package_name = package_json.binary.package_name ? package_json.binary.package_name : default_package_name;
opts.package_name = eval_template(package_name, opts);
opts.staged_tarball = path.join('build/stage', opts.remote_path, opts.package_name);
opts.hosted_path = url.resolve(opts.host, opts.remote_path);
opts.hosted_tarball = url.resolve(opts.hosted_path, opts.package_name);
return opts;
};