Skip to content
This repository was archived by the owner on Jul 28, 2021. It is now read-only.

Commit bde11a0

Browse files
committed
feat(pkgmap): move package map up and override all node_modules dirs
1 parent 219bd08 commit bde11a0

File tree

6 files changed

+96
-46
lines changed

6 files changed

+96
-46
lines changed

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function checkPkgMap () {
3434
try {
3535
const base = process.cwd()
3636
const lock = JSON.parse(stripBOM(fs.readFileSync(path.join(base, 'package-lock.json'), 'utf8')))
37-
const map = JSON.parse(stripBOM(fs.readFileSync(path.join(base, 'node_modules', '.package-map.json'), 'utf8')))
37+
const map = JSON.parse(stripBOM(fs.readFileSync(path.join(base, '.package-map.json'), 'utf8')))
3838
require('ssri').checkData(
3939
JSON.stringify(lock), map.lockfile_integrity, {error: true}
4040
)

lib/installer.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class Installer {
117117
readJson(prefix, 'package.json'),
118118
readJson(prefix, 'package-lock.json', true),
119119
readJson(prefix, 'npm-shrinkwrap.json', true),
120-
readJson(path.join(prefix, 'node_modules'), '.package-map.json', true),
120+
readJson(prefix, '.package-map.json', true),
121121
(pkg, lock, shrink, map) => {
122122
if (shrink) {
123123
this.log('verbose', 'prepare', 'using npm-shrinkwrap.json')
@@ -337,7 +337,7 @@ class Installer {
337337
const lockHash = ssri.fromData(lockStr, {algorithms: ['sha256']})
338338
const pkgMap = {
339339
'lockfile_integrity': lockHash.toString(),
340-
'path_prefix': '/.package-map.json'
340+
'path_prefix': '/node_modules'
341341
}
342342
tree.forEach((dep, next) => {
343343
if (dep.isRoot) { return next() }
@@ -364,9 +364,7 @@ class Installer {
364364
}
365365

366366
async writePackageMap (map) {
367-
const nm = path.join(this.prefix, 'node_modules')
368-
await mkdirp(nm)
369-
await writeFileAsync(path.join(nm, '.package-map.json'), stringifyPkg(map))
367+
await writeFileAsync(path.join(this.prefix, '.package-map.json'), stringifyPkg(map))
370368
}
371369
}
372370
module.exports = treeFrog

lib/node/fs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ function overrideNode () {
183183
return stats
184184
}
185185
}
186+
fs.statSync.orig = statSync
186187

187188
const {stat} = fs
188189
fs.stat = function (p, callback) {

lib/node/module.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323

2424
// NOTE: The code here is almost all identical to the regular module.js.
2525
// It's reloaded here because the `process.binding('fs')` override
26-
// can't retroactively affect module.js, and because `.package-map.json`
27-
// needs to be added to the search path. All other functionality is
26+
// can't retroactively affect module.js. All other functionality is
2827
// done by the fs override itself.
2928
module.exports.overrideNode = overrideNode
3029
function overrideNode () {
@@ -137,10 +136,6 @@ function overrideNode () {
137136

138137
let warned = false
139138
Module._findPath = function (request, paths, isMain) {
140-
paths = paths.reduce((acc, p) => {
141-
acc.push(p, path.join(p, '.package-map.json'))
142-
return acc
143-
}, [])
144139
if (path.isAbsolute(request)) {
145140
paths = ['']
146141
} else if (!paths || paths.length === 0) {

lib/pkgmap.js

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ const path = require('path')
88
const ssri = require('ssri')
99

1010
const pkgMapName = '.package-map.json'
11-
const mapNameLen = pkgMapName.length
1211
const pkgMapCache = new Map()
1312

1413
const envNoPkgMap = process.env.FROG_NO_PKG_MAP
15-
const isPkgMapDisabled = () => process.noPkgMap || envNoPkgMap
14+
const isPkgMapDisabled = () => !process.tink || process.tink.noPkgMap || envNoPkgMap
1615

1716
module.exports.resolve = resolve
1817
module.exports._clearCache = () => pkgMapCache.clear()
@@ -23,11 +22,15 @@ function resolve (...p) {
2322
// expected package + file hash.
2423
if (isPkgMapDisabled()) { return null }
2524
const resolved = path.resolve(...p)
25+
// If the file already exists in the filesystem, use the filesystem version
26+
try {
27+
(fs.statSync.orig || fs.statSync)(resolved, true)
28+
return null
29+
} catch (e) {}
2630
const result = readPkgMap(resolved)
2731
if (!result) { return result }
28-
const {pkgMap, pkgMapIdx} = result
32+
let {pkgMap, subPath} = result
2933
if (!pkgMap) { return false }
30-
let subPath = resolved.substr(pkgMapIdx - 1)
3134
let pkgName, filePath
3235
let scope = pkgMap
3336
while (subPath) {
@@ -78,7 +81,7 @@ function resolveEntity (scope, pkgName, filePath) {
7881
const next = split.shift()
7982
if (next === '.') { continue }
8083
location = location[next]
81-
if (typeof location === 'string') {
84+
if (typeof location === 'string' && !split.length) {
8285
return {hash: location, isFile: true}
8386
} else if (!location || typeof location !== 'object') {
8487
return false
@@ -92,21 +95,31 @@ function resolveEntity (scope, pkgName, filePath) {
9295
module.exports.readPkgMap = readPkgMap
9396
function readPkgMap (...p) {
9497
const resolved = path.resolve(...p)
95-
const pkgMapIdx = resolved.indexOf(pkgMapName)
96-
if (pkgMapIdx === -1 || pkgMapIdx + mapNameLen + 1 >= resolved.length) {
97-
// Not in a pkgmapped path, or reading a .package-map.json itself
98-
return null
99-
}
100-
const pkgMapPath = resolved.substr(0, pkgMapIdx + mapNameLen)
101-
let pkgMap
102-
if (pkgMapCache.has(pkgMapPath)) {
103-
pkgMap = pkgMapCache.get(pkgMapPath)
104-
} else {
105-
const p = path.toNamespacedPath ? path.toNamespacedPath(pkgMapPath) : pkgMapPath
106-
pkgMap = JSON.parse(fs.readFileSync(p))
107-
pkgMapCache.set(pkgMapPath, pkgMap)
98+
let modulesIdx = resolved.lastIndexOf('node_modules')
99+
while (modulesIdx !== -1) {
100+
let substr = resolved.substr(0, modulesIdx - 1)
101+
const pkgMapPath = path.join(substr, pkgMapName)
102+
let pkgMap
103+
if (pkgMapCache.has(pkgMapPath)) {
104+
pkgMap = pkgMapCache.get(pkgMapPath)
105+
} else {
106+
const p = path.toNamespacedPath(pkgMapPath)
107+
try {
108+
pkgMap = JSON.parse(fs.readFileSync(p))
109+
pkgMapCache.set(pkgMapPath, pkgMap)
110+
} catch (e) {
111+
if (e.code !== 'ENOENT') {
112+
throw e
113+
}
114+
}
115+
}
116+
if (pkgMap) {
117+
return {pkgMap, subPath: resolved.substr(modulesIdx - 1)}
118+
} else {
119+
modulesIdx = substr.lastIndexOf('node_modules')
120+
}
108121
}
109-
return {pkgMap, pkgMapIdx}
122+
return null
110123
}
111124

112125
module.exports.read = read
@@ -143,15 +156,15 @@ function readSync ({cache, hash, pkg, resolvedPath, isFile}) {
143156

144157
module.exports.stat = stat
145158
async function stat ({cache, hash, pkg, resolvedPath, isDir}, verify) {
146-
if (!cache || !hash) {
147-
throw new Error('stat() requires a fully-resolved pkgmap file address')
148-
}
149159
if (isDir || path.basename(resolvedPath) === '.package-map.json') {
150160
return Object.assign(fs.lstatSync(process.tink.cache), {
151161
mode: 16676, // read-only
152162
size: 64
153163
})
154164
}
165+
if (!cache || !hash) {
166+
throw new Error('stat() requires a fully-resolved pkgmap file address')
167+
}
155168
let info
156169
try {
157170
info = await ccRead.hasContent(cache, hash)
@@ -188,15 +201,15 @@ async function stat ({cache, hash, pkg, resolvedPath, isDir}, verify) {
188201

189202
module.exports.statSync = statSync
190203
function statSync ({cache, hash, pkg, resolvedPath, isDir}, verify) {
191-
if (!cache || !hash) {
192-
throw new Error('statSync() requires a fully-resolved pkgmap file address')
193-
}
194204
if (isDir || path.basename(resolvedPath) === '.package-map.json') {
195205
return Object.assign(fs.lstatSync(process.tink.cache), {
196206
mode: 16676, // read-only
197207
size: 64
198208
})
199209
}
210+
if (!cache || !hash) {
211+
throw new Error('statSync() requires a fully-resolved pkgmap file address')
212+
}
200213
let info
201214
try {
202215
info = ccRead.hasContent.sync(cache, hash)

test/pkgmap.js

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,58 @@ const {File, Dir} = Tacks
1111

1212
const pkgmap = require('../lib/pkgmap.js')
1313

14+
test('UNIT readPkgMap', t => {
15+
process.tink = {
16+
cache: './here'
17+
}
18+
const pkgMap = {
19+
path_prefix: '/node_modules',
20+
packages: {
21+
'eggplant': {
22+
files: {
23+
'hello.js': 'sha1-deadbeef'
24+
}
25+
}
26+
},
27+
scopes: {
28+
'eggplant': {
29+
path_prefix: '/node_modules',
30+
packages: {
31+
'aubergine': {
32+
files: {
33+
'bonjour.js': 'sha1-badc0ffee'
34+
}
35+
},
36+
'@myscope/scoped': {
37+
files: {
38+
'ohmy.js': 'sha1-abcdef',
39+
'lib': {
40+
'hithere.js': 'sha1-badbebe'
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
const fixture = new Tacks(Dir({
49+
'.package-map.json': File(pkgMap)
50+
}))
51+
fixture.create(testDir.path)
52+
t.deepEqual(pkgmap.readPkgMap('node_modules/eggplant'), {
53+
pkgMap,
54+
subPath: '/node_modules/eggplant'
55+
})
56+
t.done()
57+
})
58+
1459
test('resolve: finds an existing path into a .package-map.json', async t => {
1560
process.tink = {
1661
cache: './here'
1762
}
1863
const fixture = new Tacks(Dir({
1964
'.package-map.json': File({
20-
path_prefix: '/.package-map.json',
65+
path_prefix: '/node_modules',
2166
packages: {
2267
'eggplant': {
2368
files: {
@@ -48,7 +93,7 @@ test('resolve: finds an existing path into a .package-map.json', async t => {
4893
})
4994
}))
5095
fixture.create(testDir.path)
51-
const prefix = path.join(testDir.path, '.package-map.json', 'eggplant')
96+
const prefix = path.join(testDir.path, 'node_modules', 'eggplant')
5297
t.similar(pkgmap.resolve(prefix, 'hello.js'), {
5398
cache: './here',
5499
hash: 'sha1-deadbeef',
@@ -90,9 +135,7 @@ test('resolve: finds an existing path into a .package-map.json', async t => {
90135
}
91136
}, 'found file even though pkgmap deleted')
92137
pkgmap._clearCache()
93-
t.throws(() => {
94-
pkgmap.resolve(prefix, 'hello.js')
95-
}, /ENOENT/, 'cache gone after clearing')
138+
t.equal(pkgmap.resolve(prefix, 'hello.js'), null, 'cache gone')
96139
pkgmap._clearCache()
97140
})
98141

@@ -104,7 +147,7 @@ test('read: reads a file defined in a package map', async t => {
104147
}
105148
const fixture = new Tacks(Dir({
106149
'.package-map.json': File({
107-
path_prefix: '/.package-map.json',
150+
path_prefix: '/node_modules',
108151
packages: {
109152
'eggplant': {
110153
files: {
@@ -116,7 +159,7 @@ test('read: reads a file defined in a package map', async t => {
116159
}))
117160
fixture.create(testDir.path)
118161
const p = pkgmap.resolve(
119-
testDir.path, '.package-map.json', 'eggplant', 'hello.js'
162+
testDir.path, 'node_modules', 'eggplant', 'hello.js'
120163
)
121164
t.equal(
122165
(await pkgmap.read(p)).toString('utf8'),
@@ -141,7 +184,7 @@ test('stat: get filesystem stats for a file', async t => {
141184
}
142185
const fixture = new Tacks(Dir({
143186
'.package-map.json': File({
144-
path_prefix: '/.package-map.json',
187+
path_prefix: '/node_modules',
145188
packages: {
146189
'eggplant': {
147190
files: {
@@ -153,7 +196,7 @@ test('stat: get filesystem stats for a file', async t => {
153196
}))
154197
fixture.create(testDir.path)
155198
const p = pkgmap.resolve(
156-
testDir.path, '.package-map.json', 'eggplant', 'hello.js'
199+
testDir.path, 'node_modules', 'eggplant', 'hello.js'
157200
)
158201
const stat = await pkgmap.stat(p)
159202
t.ok(stat, 'got stat from cache file')

0 commit comments

Comments
 (0)