Skip to content

Instantly share code, notes, and snippets.

@Jyrno42
Created December 22, 2021 12:34
Show Gist options
  • Save Jyrno42/4bbcbc9c92f790e7b7e5949d3ba046d2 to your computer and use it in GitHub Desktop.
Save Jyrno42/4bbcbc9c92f790e7b7e5949d3ba046d2 to your computer and use it in GitHub Desktop.
Source relative absolute paths in parcel

With the resolver plugin below it is possible to have source relative imports in parcel (e.g. resolver.modules from webpack).

This allows one to import their files without using relative imports. For example, given this configration in package.json:

{
  ...,
  "resolver-src-root": {
    "root": "/app",
    "extensions": [".ts", ".tsx", ".js", ".jsx", ".json"]
  }
}

and the following project structure:

package.json
/app
-- /components
---- /some
------ componentA.js
---- /another
------ componentB.js
-- api.js

The components can import eachother like this:

import ComponentA from "components/some/componentA"; // from componentB.js
import componentB from "components/another/componentB"; // from componentA.js

and the file api.js can be imported by both like this:

import api from "api";
const path = require('path');
const { Resolver } = require('@parcel/plugin');
const NodeResolver = require('@parcel/node-resolver-core').default;
const { loadConfig } = require('./common');
let config = null;
const shouldHandleCache = new Set();
module.exports = new Resolver({
expandFile(file, extensions) {
// Expand extensions and aliases
let res = [];
for (let ext of extensions) {
let f = file + ext;
res.push(f);
}
if (path.extname(file)) {
res.unshift(file);
} else {
res.push(file);
}
return res;
},
async resolve({ dependency, options, specifier }) {
if (
specifier.startsWith('/') ||
specifier.startsWith('.') ||
specifier.startsWith('~') ||
specifier.startsWith('@')
) {
return null;
}
if (!config) {
config = loadConfig();
}
const extensions =
dependency.specifierType === 'commonjs' || dependency.specifierType === 'esm'
? config.extensions.map(x => x.substr(1))
: [];
const fs = options.inputFS;
const filePath = path.join(config.baseDir, specifier);
const cached = shouldHandleCache.has(specifier);
const existsInSrcRoot =
cached ||
fs.existsSync(filePath) ||
(specifier.includes(path.sep) && fs.existsSync(path.dirname(filePath))) ||
fs.findFirstFile(this.expandFile(filePath, config.extensions));
if (existsInSrcRoot) {
if (!cached) {
shouldHandleCache.add(specifier);
}
const resolver = new NodeResolver({
fs,
projectRoot: options.projectRoot,
// Extensions are always required in URL dependencies.
extensions,
mainFields: ['source', 'browser', 'module', 'main'],
});
return resolver.resolve({
filename: `${config.absRoot}/${specifier}`,
specifierType: dependency.specifierType,
parent: dependency.resolveFrom,
env: dependency.env,
sourcePath: dependency.sourcePath,
});
}
// Let the next resolver in the pipeline handle
// this dependency.
return null;
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment