Last active
October 7, 2021 02:24
-
-
Save NoriSte/f9faf153e5cba3fb1c1b1675408cd748 to your computer and use it in GitHub Desktop.
Mt very first plugin for Vite, discuss it on Twitter https://twitter.com/NoriSte/status/1445752344596021255
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { Plugin } from 'vite' | |
import path from 'path' | |
const name = 'prevent-server-data-circular-imports' | |
const defaultOptions = { verbose: false } | |
// Detect if the module is in server-data | |
const serverDataFileRegexp = /(.*)?server-data\/src\// | |
// Extract the imported path | |
/* | |
import # every line starting with "import" | |
[\s\S] # everything, including line breaks | |
*? # any number of times, but greedy | |
# (greedy stopping at the first `from` occurrence) | |
from # matches `from` | |
(\"|') # followed by a space and a single or double quote | |
(?<path>.*) # capture the path and name it | |
(\"|') # matches the closing single/double quote | |
*/ | |
const importPathRegexp = /import[\s\S]*?from (\"|')(?<path>.*)(\"|')/gim | |
function createLog(verbose: boolean) { | |
if (verbose) { | |
return (...params) => { | |
console.log(`[${name}]`, ...params) | |
} | |
} | |
return () => {} | |
} | |
/** | |
* This prevent the following build error | |
* FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory | |
* caused by a circular dependency. | |
* | |
* The error does not appear while developing, and it's caused by server-data modules that import | |
* from `server-data/src/index.ts` instead of pointing directly to the correct module. | |
* | |
* The purpose of `server-data/src/index.ts` is exposing a portion of its modules to the other | |
* packages and must not be used directly by the server-data modules. | |
* | |
* @param {Object} passedOptions Enable verbose logging | |
* @param {boolean} [passedOptions.verbose=false] | |
*/ | |
export const preventServerDataCircularImports = (passedOptions = {}): Plugin => { | |
const options = { | |
...defaultOptions, | |
...passedOptions, | |
} | |
const log = createLog(options.verbose) | |
return { | |
name, | |
// `fullFilePath` example: /Volumes/dev/route-manager/local/server-data/src/store/createStore.ts | |
transform(fileContent, fullFilePath) { | |
// Bailout for files out of server-data | |
if (!serverDataFileRegexp.test(fullFilePath)) { | |
log(`The module is not a server-data one ${fullFilePath}`) | |
return null | |
} | |
log(`Parsing ${fullFilePath}`) | |
let importCount = 0 | |
let importMatch: RegExpExecArray | null | |
// loops through all the imports | |
while ((importMatch = importPathRegexp.exec(fileContent))) { | |
importCount++ | |
// ex. createStore.ts | |
const fileName = path.parse(fullFilePath).base | |
// ex. /Volumes/dev/route-manager/local/server-data/src/store/ | |
const filePath = fullFilePath.replace(fileName, '') | |
/** | |
* Detect the absolute imports. | |
* | |
* @example | |
* import {...} from '@/local/server-data/index' | |
*/ | |
if (importMatch.groups.path === '@/local/server-data/index') { | |
throw new Error( | |
`Server-data modules cannot import from server-data/src/index! File: ${fullFilePath} - Import: ${importMatch.groups.path}`, | |
) | |
} | |
// ex. ../../ | |
const importPath = path.join(filePath, importMatch.groups.path) | |
/** | |
* Detect the relative imports | |
* | |
* @example | |
* import {...} from '../' // from server-data/src/store/createStore.ts | |
*/ | |
if (importPath.endsWith('/local/server-data/src/')) { | |
throw new Error( | |
`Server-data modules cannot import from server-data/src/index! File: ${fullFilePath} - Import: ${importMatch.groups.path}`, | |
) | |
} | |
log(`Safe import ${importMatch[0]}`) | |
} | |
if (importCount === 0) { | |
log(`No imports found`) | |
} | |
// line break | |
log('') | |
// Nothing to do | |
return null | |
}, | |
} | |
} | |
// eslint-disable-next-line import/no-default-export | |
export default preventServerDataCircularImports | |
exports.default = preventServerDataCircularImports |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment