Skip to content

Commit

Permalink
feat: support offline mode (#457)
Browse files Browse the repository at this point in the history
closes #456
  • Loading branch information
fengmk2 authored May 2, 2023
1 parent 4c6946e commit d7c52ee
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 3 deletions.
7 changes: 7 additions & 0 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Object.assign(argv, parseArgs(originalArgv, {
'save-dependencies-tree',
'force-link-latest',
'workspaces',
'offline',
],
default: {
optional: true,
Expand Down Expand Up @@ -142,6 +143,7 @@ Options:
--prune: prune unnecessary files from ./node_modules, such as markdown, typescript source files, and so on.
--dependencies-tree: install with dependencies tree to restore the last install.
--force-link-latest: force link latest version package to module root path.
--offline: offline mode. If a package won't be found locally, the installation will fail.
`
);
process.exit(0);
Expand Down Expand Up @@ -313,6 +315,7 @@ debug('argv: %j, env: %j', argv, env);
config.detail = true;
}
config.client = argv.client;
config.offline = !!argv.offline;

if (argv['tarball-url-mapping']) {
const tarballUrlMapping = JSON.parse(argv['tarball-url-mapping']);
Expand Down Expand Up @@ -352,6 +355,10 @@ debug('argv: %j, env: %j', argv, env);
}
});

if (config.offline) {
console.warn(chalk.yellow('npminstall WARN running on offline mode'));
}

// -g install to npm's global prefix
if (argv.global) {
// support custom prefix for global install
Expand Down
11 changes: 8 additions & 3 deletions lib/download/npm.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const debug = require('node:util').debuglog('npminstall:download:npm');
const bytes = require('bytes');
const { randomUUID } = require('node:crypto');
const { createWriteStream, createReadStream, rmSync } = require('node:fs');
const fs = require('node:fs/promises');
const path = require('node:path');
const crypto = require('node:crypto');
const urlresolve = require('node:url').resolve;
const { resolve: urlresolve } = require('node:url');
const zlib = require('node:zlib');
const os = require('node:os');
const bytes = require('bytes');
const tar = require('tar');
const destroy = require('destroy');
const chalk = require('chalk');
Expand Down Expand Up @@ -181,6 +181,10 @@ async function removeCacheInfo(fullname, globalOptions) {

async function getFullPackageMeta(fullname, globalOptions) {
const info = await _getCacheInfo(fullname, globalOptions);
if (globalOptions.offline && !info.cache) {
throw new Error(`Can\'t find package ${fullname} manifests on offline mode`);
}

if (!info.cacheFile) {
const result = await _fetchFullPackageMeta(info.pkgUrl, globalOptions);
return result.data;
Expand All @@ -190,7 +194,8 @@ async function getFullPackageMeta(fullname, globalOptions) {
return await _fetchFullPackageMetaWithCache(info.pkgUrl, globalOptions, info.cacheFile);
}
// check is expired or not
if (info.cache.expired > Date.now()) {
// offline should force to use cache manifests
if (globalOptions.offline || info.cache.expired > Date.now()) {
globalOptions.totalCacheJSONCount += 1;
return info.cache.manifests;
}
Expand Down
1 change: 1 addition & 0 deletions lib/local_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const { runLifecycleScripts } = require('./lifecycle_scripts');
* - {Boolean} [trace] - show memory and cpu usages traces of installation
* - {Boolean} [flatten] - flatten dependencies by matching ancestors' dependencies
* - {Boolean} [disableFallbackStore] - disable fallback store, default is `false`
* - {Boolean} [offline] - offline mode, default is `false`
* @param {Object} context - install context
*/
module.exports = async (options, context = new Context()) => {
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/install-offline/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

console.log(
require.resolve('koa'),
!!require('koa')
);
14 changes: 14 additions & 0 deletions test/fixtures/install-offline/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "install-offline",
"version": "1.0.0",
"scripts": {
"postinstall": "node index.js"
},
"dependencies": {
"debug": "~2.2.0",
"koa": "^1.1.2"
},
"devDependencies": {
"co": "^4.6.0"
}
}
56 changes: 56 additions & 0 deletions test/install-offline.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const path = require('node:path');
const coffee = require('coffee');
const helper = require('./helper');

describe('test/install-offline.test.js', () => {
const [ homedir, cleanupTmp ] = helper.tmp();
const demo = helper.fixtures('install-offline');
const cleanupModules = helper.cleanup(demo);

async function cleanup() {
await cleanupModules();
await cleanupTmp();
}

beforeEach(cleanup);
afterEach(cleanup);

it('should install fail when cache manifests not exists', async () => {
await coffee.fork(helper.npminstall, [ '--offline' ], {
cwd: demo,
env: Object.assign({}, process.env, {
npm_config_cache: path.join(homedir, 'foocache/.npminstall_tarball'),
}),
})
.debug()
.expect('code', 1)
.expect('stderr', /Can\'t find package .+? manifests on offline mode/)
.end();
});

it('should install success when cache manifests exists', async () => {
await coffee.fork(helper.npminstall, [ '-d' ], {
cwd: demo,
env: Object.assign({}, process.env, {
npm_config_cache: path.join(homedir, 'foocache/.npminstall_tarball'),
}),
})
.debug()
.expect('code', 0)
.expect('stdout', /All packages installed/)
.end();

await cleanupModules();
await coffee.fork(helper.npminstall, [ '-d', '--offline' ], {
cwd: demo,
env: Object.assign({}, process.env, {
npm_config_cache: path.join(homedir, 'foocache/.npminstall_tarball'),
}),
})
.debug()
.expect('code', 0)
.expect('stdout', /All packages installed/)
.expect('stdout', /speed 0B\/s, json 0\(0B\), tarball 0B, manifests cache hit \d+, etag hit 0 \/ miss 0/)
.end();
});
});

0 comments on commit d7c52ee

Please sign in to comment.