Last active
December 9, 2019 00:40
-
-
Save ScriptedAlchemy/b46d20a46d29ab72397b1bd3331382ed to your computer and use it in GitHub Desktop.
Module plugin
This file contains hidden or 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
| const path = require('path'); | |
| const VirtualStats = require('./virtual-stats'); | |
| class SyntheticPlugin { | |
| constructor(options) { | |
| this.options = options; | |
| } | |
| apply(compiler) { | |
| const moduleName = this.options.moduleName; | |
| const ctime = SyntheticPlugin.statsDate(); | |
| let modulePath = this.options.path; | |
| let contents; | |
| if (typeof this.options.contents === 'string') { | |
| contents = this.options.contents; | |
| } | |
| if (typeof this.options.contents === 'object') { | |
| if (typeof this.options.contents.then !== 'function') { | |
| contents = JSON.stringify(this.options.contents); | |
| } | |
| } | |
| if (typeof this.options.contents === 'function') { | |
| contents = this.options.contents(); | |
| } | |
| if (typeof contents === 'string') { | |
| contents = Promise.resolve(contents); | |
| } | |
| function resolverPlugin(request, cb) { | |
| // populate the file system cache with the virtual module | |
| const fs = (this && this.fileSystem) || compiler.inputFileSystem; | |
| const join = (this && this.join) || path.join; | |
| // webpack 1.x compatibility | |
| if (typeof request === 'string') { | |
| request = cb; | |
| cb = null; | |
| } | |
| if (!modulePath) { | |
| modulePath = join(compiler.context, moduleName); | |
| } | |
| const resolve = (data) => { | |
| SyntheticPlugin.populateFilesystem({ fs, modulePath, contents: data, ctime }); | |
| }; | |
| const resolved = contents.then(resolve); | |
| if (!cb) { | |
| return; | |
| } | |
| resolved.then(() => cb()); | |
| } | |
| const waitForResolvers = !compiler.resolvers.normal; | |
| function addPlugin() { | |
| const useModuleFactory = !compiler.resolvers.normal.plugin; | |
| if (useModuleFactory) { | |
| if (compiler.hooks) { | |
| compiler.hooks.normalModuleFactory.tap('SyntheticPlugin', (nmf) => { | |
| nmf.hooks.beforeResolve.tap('SyntheticPlugin', resolverPlugin); | |
| }); | |
| } else { | |
| compiler.plugin('normal-module-factory', (nmf) => { | |
| nmf.plugin('before-resolve', resolverPlugin); | |
| }); | |
| } | |
| } else { | |
| compiler.resolvers.normal.plugin('before-resolve', resolverPlugin); | |
| } | |
| } | |
| if (waitForResolvers) { | |
| compiler.plugin('after-resolvers', addPlugin); | |
| } else { | |
| addPlugin(); | |
| } | |
| } | |
| static populateFilesystem(options) { | |
| const fs = options.fs; | |
| const modulePath = options.modulePath; | |
| const contents = options.contents; | |
| const mapIsAvailable = typeof Map !== 'undefined'; | |
| const statStorageIsMap = mapIsAvailable && fs._statStorage.data instanceof Map; | |
| const readFileStorageIsMap = mapIsAvailable && fs._readFileStorage.data instanceof Map; | |
| if (readFileStorageIsMap) { // [email protected] or greater | |
| if (fs._readFileStorage.data.has(modulePath)) { | |
| return; | |
| } | |
| } else if (fs._readFileStorage.data[modulePath]) { // [email protected] or lower | |
| return; | |
| } | |
| const stats = SyntheticPlugin.createStats(options); | |
| if (statStorageIsMap) { // [email protected] or greater | |
| fs._statStorage.data.set(modulePath, [null, stats]); | |
| } else { // [email protected] or lower | |
| fs._statStorage.data[modulePath] = [null, stats]; | |
| } | |
| if (readFileStorageIsMap) { // [email protected] or greater | |
| fs._readFileStorage.data.set(modulePath, [null, contents]); | |
| } else { // [email protected] or lower | |
| fs._readFileStorage.data[modulePath] = [null, contents]; | |
| } | |
| } | |
| static statsDate(inputDate) { | |
| if (!inputDate) { | |
| inputDate = new Date(); | |
| } | |
| return inputDate.toString(); | |
| } | |
| static createStats(options) { | |
| if (!options) { | |
| options = {}; | |
| } | |
| if (!options.ctime) { | |
| options.ctime = SyntheticPlugin.statsDate(); | |
| } | |
| if (!options.mtime) { | |
| options.mtime = SyntheticPlugin.statsDate(); | |
| } | |
| if (!options.size) { | |
| options.size = 0; | |
| } | |
| if (!options.size && options.contents) { | |
| options.size = options.contents.length; | |
| } | |
| return new VirtualStats({ | |
| dev: 8675309, | |
| nlink: 1, | |
| uid: 501, | |
| gid: 20, | |
| rdev: 0, | |
| blksize: 4096, | |
| ino: 44700000, | |
| mode: 33188, | |
| size: options.size, | |
| atime: options.mtime, | |
| mtime: options.mtime, | |
| ctime: options.ctime, | |
| birthtime: options.ctime, | |
| }); | |
| } | |
| } | |
| module.exports = SyntheticPlugin; |
This file contains hidden or 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
| // some file in the app | |
| var moduleFoo = require('someModule'); | |
| console.log(someModule.thing); |
This file contains hidden or 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
| const constants = require('constants'); | |
| class VirtualStats { | |
| /** | |
| * Create a new stats object. | |
| * @param {Object} config Stats properties. | |
| * @constructor | |
| */ | |
| constructor(config) { | |
| for (const key in config) { | |
| if (!config.hasOwnProperty(key)) { | |
| continue; | |
| } | |
| this[key] = config[key]; | |
| } | |
| } | |
| /** | |
| * Check if mode indicates property. | |
| * @param {number} property Property to check. | |
| * @return {boolean} Property matches mode. | |
| */ | |
| _checkModeProperty(property) { | |
| return ((this.mode & constants.S_IFMT) === property); | |
| } | |
| /** | |
| * @return {Boolean} Is a directory. | |
| */ | |
| isDirectory() { | |
| return this._checkModeProperty(constants.S_IFDIR); | |
| } | |
| /** | |
| * @return {Boolean} Is a regular file. | |
| */ | |
| isFile() { | |
| return this._checkModeProperty(constants.S_IFREG); | |
| } | |
| /** | |
| * @return {Boolean} Is a block device. | |
| */ | |
| isBlockDevice() { | |
| return this._checkModeProperty(constants.S_IFBLK); | |
| } | |
| /** | |
| * @return {Boolean} Is a character device. | |
| */ | |
| isCharacterDevice() { | |
| return this._checkModeProperty(constants.S_IFCHR); | |
| } | |
| /** | |
| * @return {Boolean} Is a symbolic link. | |
| */ | |
| isSymbolicLink() { | |
| return this._checkModeProperty(constants.S_IFLNK); | |
| } | |
| /** | |
| * @return {Boolean} Is a named pipe. | |
| */ | |
| isFIFO() { | |
| return this._checkModeProperty(constants.S_IFIFO); | |
| } | |
| /** | |
| * @return {Boolean} Is a socket. | |
| */ | |
| isSocket() { | |
| return this._checkModeProperty(constants.S_IFSOCK); | |
| } | |
| } | |
| module.exports = VirtualStats; |
This file contains hidden or 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
| var SynthericPlugin = require('magic-modules'); | |
| var vmds = new SyntheticPlugin({ | |
| 'node_modules/someModule.js': 'module.exports = { thing: "test" };', | |
| 'node_modules/anotherModule.js': 'module.exports = { other: "thinges" };', | |
| 'pages/blog.js': 'module.exports = 'require("@myorg/blog")' | |
| }); | |
| module.exports = { | |
| // ... | |
| plugins: [ | |
| vmds | |
| ] | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment