UNPKG

5.27 kBJavaScriptView Raw
1const path = require(`path`);
2const {resolveModuleName} = require(`ts-pnp`);
3
4let pnp;
5
6try {
7 pnp = require(`pnpapi`);
8} catch (error) {
9 // not in PnP; not a problem
10}
11
12function nothing() {
13 // ¯\_(ツ)_/¯
14}
15
16function getModuleLocator(module) {
17 const moduleLocation = typeof module === `string`
18 ? module
19 : module.filename;
20
21 if (!moduleLocation)
22 throw new Error(`The specified module doesn't seem to exist on the filesystem`);
23
24 const moduleLocator = pnp.findPackageLocator(moduleLocation);
25
26 if (!moduleLocator)
27 throw new Error(`the specified module doesn't seem to be part of the dependency tree`);
28
29 return moduleLocator;
30}
31
32function getDependencyLocator(sourceLocator, name) {
33 const {packageDependencies} = pnp.getPackageInformation(sourceLocator);
34 const reference = packageDependencies.get(name);
35
36 return {name, reference};
37}
38
39function getSourceLocation(sourceLocator) {
40 if (!sourceLocator)
41 return null;
42
43 const sourceInformation = pnp.getPackageInformation(sourceLocator);
44
45 if (!sourceInformation)
46 throw new Error(`Couldn't find the package to use as resolution source`);
47
48 if (!sourceInformation.packageLocation)
49 throw new Error(`The package to use as resolution source seem to not have been installed - maybe it's a devDependency not installed in prod?`);
50
51 return sourceInformation.packageLocation.replace(/\/?$/, `/`);
52}
53
54function makeResolver(sourceLocator, filter) {
55 const sourceLocation = getSourceLocation(sourceLocator);
56
57 return resolver => {
58 const BACKWARD_PATH = /^\.\.([\\\/]|$)/;
59
60 const resolvedHook = resolver.ensureHook(`resolve`);
61
62 // Prevents the SymlinkPlugin from kicking in. We need the symlinks to be preserved because that's how we deal with peer dependencies ambiguities.
63 resolver.getHook(`file`).intercept({
64 register: tapInfo => {
65 return tapInfo.name !== `SymlinkPlugin` ? tapInfo : Object.assign({}, tapInfo, {fn: (request, resolveContext, callback) => {
66 callback();
67 }});
68 }
69 });
70
71 resolver.getHook(`after-module`).tapAsync(`PnpResolver`, (request, resolveContext, callback) => {
72 // rethrow pnp errors if we have any for this request
73 return callback(resolveContext.pnpErrors && resolveContext.pnpErrors.get(request.context.issuer));
74 });
75
76 // Register a plugin that will resolve bare imports into the package location on the filesystem before leaving the rest of the resolution to Webpack
77 resolver.getHook(`before-module`).tapAsync(`PnpResolver`, (requestContext, resolveContext, callback) => {
78 let request = requestContext.request;
79 let issuer = requestContext.context.issuer;
80
81 // When using require.context, issuer seems to be false (cf https://github.com/webpack/webpack-dev-server/blob/d0725c98fb752d8c0b1e8c9067e526e22b5f5134/client-src/default/index.js#L94)
82 if (!issuer) {
83 issuer = `${requestContext.path}/`;
84 // We only support issuer when they're absolute paths. I'm not sure the opposite can ever happen, but better check here.
85 } else if (!path.isAbsolute(issuer)) {
86 throw new Error(`Cannot successfully resolve this dependency - issuer not supported (${issuer})`);
87 }
88
89 if (filter) {
90 const relative = path.relative(filter, issuer);
91 if (path.isAbsolute(relative) || BACKWARD_PATH.test(relative)) {
92 return callback(null);
93 }
94 }
95
96 let resolutionIssuer = sourceLocation || issuer;
97 let resolution;
98
99 try {
100 resolution = pnp.resolveToUnqualified(request, resolutionIssuer, {considerBuiltins: false});
101 } catch (error) {
102 if (resolveContext.missingDependencies)
103 resolveContext.missingDependencies.add(requestContext.path);
104
105 if (resolveContext.log)
106 resolveContext.log(error.message);
107
108 resolveContext.pnpErrors = resolveContext.pnpErrors || new Map();
109 resolveContext.pnpErrors.set(issuer, error);
110
111 return callback();
112 }
113
114 resolver.doResolve(
115 resolvedHook,
116 Object.assign({}, requestContext, {
117 request: resolution,
118 }),
119 null,
120 resolveContext,
121 callback
122 );
123 });
124 };
125}
126
127module.exports = pnp ? {
128 apply: makeResolver(null),
129} : {
130 apply: nothing,
131};
132
133module.exports.makePlugin = (locator, filter) => pnp ? {
134 apply: makeResolver(locator, filter),
135} : {
136 apply: nothing,
137};
138
139module.exports.moduleLoader = module => pnp ? {
140 apply: makeResolver(getModuleLocator(module)),
141} : {
142 apply: nothing,
143};
144
145module.exports.topLevelLoader = pnp ? {
146 apply: makeResolver(pnp.topLevel),
147} : {
148 apply: nothing,
149};
150
151module.exports.bind = (filter, module, dependency) => pnp ? {
152 apply: makeResolver(dependency ? getDependencyLocator(getModuleLocator(module), dependency) : getModuleLocator(module), filter),
153} : {
154 apply: nothing,
155};
156
157module.exports.tsLoaderOptions = (options = {}) => pnp ? Object.assign({}, options, {
158 resolveModuleName: resolveModuleName,
159 resolveTypeReferenceDirective: resolveModuleName,
160}) : options;
161
162module.exports.forkTsCheckerOptions = (options = {}) => pnp ? Object.assign({}, options, {
163 resolveModuleNameModule: require.resolve(`./ts`),
164 resolveTypeReferenceDirectiveModule: require.resolve(`./ts`),
165}) : options;