UNPKG

parcel-plugin-import-maps

Version:

A plugin for Parcel to use import maps for microfrontends.

199 lines (165 loc) 4.81 kB
const { resolve, relative } = require("path"); const { writeFileSync, mkdirSync, existsSync } = require("fs"); function resolveImportMap(dir) { const pckg = getPackageJson(dir); const map = pckg.importmap; if (typeof map === "string") { const target = resolve(dir, map); if (existsSync(target)) { return require(target); } else { console.warn( `Could not find the referenced import maps "${map}" from ${dir}. Skipping.` ); } } else if (typeof map === "object") { return map; } return undefined; } function getPackageJson(dir) { if (dir) { const path = resolve(dir, "package.json"); return require(path); } return {}; } function getPackageDir(dir) { const path = resolve(dir, "package.json"); if (existsSync(path)) { return dir; } const upper = resolve(dir, ".."); if (upper !== dir) { return getPackageDir(upper); } return undefined; } function createFile(dir, name, content) { const path = resolve(dir, `${name}.js`); writeFileSync(path, content, "utf8"); return path; } function getSourcePath(root, file) { return `/${relative(root, file)}`; } function resolvePath(root, dir, file) { if (file.startsWith('/')) { return getSourcePath(root, `.${file}`); } else { return getSourcePath(root, resolve(dir, file)); } } function getHashFor(root, dir, file) { if (!file.startsWith('http:') && !file.startsWith('https:')) { const path = resolvePath(root, dir, file); return `require.resolve(${JSON.stringify(path)})[0][0]`; } else { return JSON.stringify(file); } } function getImportFor(root, dir, file) { if (!file.startsWith('http:') && !file.startsWith('https:')) { const path = resolvePath(root, dir, file); return `import(${JSON.stringify(path)})`; } else { return `new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", ${JSON.stringify(file)}); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { const result = { exports: {}, }; const f = new Function('module', 'exports', 'require', xhr.responseText); f(result, result.exports, require); resolve(result.exports); } else { reject(xhr.statusText); } } }; xhr.send(); })`; } } let original; module.exports = function(bundler) { const root = bundler.options.rootDir; const dir = getPackageDir(root); const map = resolveImportMap(dir); const keys = Object.keys((map && map.imports) || {}); if (keys.length > 0) { const modules = {}; const temp = resolve(__dirname, "_temp"); const resolver = bundler.resolver; const getAlias = original || (original = resolver.__proto__.getAlias); if (!existsSync(temp)) { mkdirSync(temp); } createFile( temp, "index", ` if (!window.__importMaps) { const imports = []; window.__importMaps = true; window.__resolveImport = function (id) { for (const item of imports) { if (item.id === id) { return item; } } return {}; }; window.__registerImports = function (newImports) { newImports.forEach(i => { if (!imports.some(j => j.id === i.id)) { const item = { id: i.id, data: undefined, }; item.loading = i.load().then(data => (item.data = data), err => console.error(err)); imports.push(item); } }); }; } const localImports = [${keys.map(id => `{ id: ${getHashFor(root, dir, map.imports[id])}, ref: ${JSON.stringify(id)}, load: () => ${getImportFor(root, dir, map.imports[id])}, }`).join(',')}]; window.__registerImports(localImports); function getId(ref) { const [id] = localImports.filter(i => i.ref === ref).map(i => i.id); return id; } exports.ready = function (ref) { const refs = Array.isArray(ref) ? ref : (ref ? [ref] : localImports.map(i => i.ref)); return Promise.all(refs.map(ref => window.__resolveImport(getId(ref)).loading)); }; exports.resolve = function (ref) { return window.__resolveImport(getId(ref)).data; } ` ); resolver.__proto__.getAlias = function(filename, dir, aliases) { if (filename === 'importmap') { return resolve(temp, 'index.js'); } else if (keys.includes(filename)) { const m = modules[filename]; if (!m) { return (modules[filename] = createFile( temp, Object.keys(modules).length.toString(), `module.exports = require('importmap').resolve(${JSON.stringify(filename)})`, )); } return m; } return getAlias.call(resolver, filename, dir, aliases); }; } };