-
-
Notifications
You must be signed in to change notification settings - Fork 26.9k
/
ModuleScopePlugin.js
107 lines (102 loc) · 3.43 KB
/
ModuleScopePlugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const chalk = require('chalk');
const path = require('path');
const os = require('os');
class ModuleScopePlugin {
constructor(appSrc, allowedFiles = []) {
this.appSrcs = Array.isArray(appSrc) ? appSrc : [appSrc];
this.allowedFiles = new Set(allowedFiles);
this.allowedPaths = [...allowedFiles]
.map(path.dirname)
.filter(p => path.relative(p, process.cwd()) !== '');
}
apply(resolver) {
const { appSrcs } = this;
resolver.hooks.file.tapAsync(
'ModuleScopePlugin',
(request, contextResolver, callback) => {
// Unknown issuer, probably webpack internals
if (!request.context.issuer) {
return callback();
}
if (
// If this resolves to a node_module, we don't care what happens next
request.descriptionFileRoot.indexOf('/node_modules/') !== -1 ||
request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 ||
// Make sure this request was manual
!request.__innerRequest_request
) {
return callback();
}
// Resolve the issuer from our appSrc and make sure it's one of our files
// Maybe an indexOf === 0 would be better?
if (
appSrcs.every(appSrc => {
const relative = path.relative(appSrc, request.context.issuer);
// If it's not in one of our app src or a subdirectory, not our request!
return relative.startsWith('../') || relative.startsWith('..\\');
})
) {
return callback();
}
const requestFullPath = path.resolve(
path.dirname(request.context.issuer),
request.__innerRequest_request
);
if (this.allowedFiles.has(requestFullPath)) {
return callback();
}
if (
this.allowedPaths.some(allowedFile => {
return requestFullPath.startsWith(allowedFile);
})
) {
return callback();
}
// Find path from src to the requested file
// Error if in a parent directory of all given appSrcs
if (
appSrcs.every(appSrc => {
const requestRelative = path.relative(appSrc, requestFullPath);
return (
requestRelative.startsWith('../') ||
requestRelative.startsWith('..\\')
);
})
) {
const scopeError = new Error(
`You attempted to import ${chalk.cyan(
request.__innerRequest_request
)} which falls outside of the project ${chalk.cyan(
'src/'
)} directory. ` +
`Relative imports outside of ${chalk.cyan(
'src/'
)} are not supported.` +
os.EOL +
`You can either move it inside ${chalk.cyan(
'src/'
)}, or add a symlink to it from project's ${chalk.cyan(
'node_modules/'
)}.`
);
Object.defineProperty(scopeError, '__module_scope_plugin', {
value: true,
writable: false,
enumerable: false,
});
callback(scopeError, request);
} else {
callback();
}
}
);
}
}
module.exports = ModuleScopePlugin;