Skip to content

Commit 20b3dca

Browse files
committed
Rework plugin to produce a virtual module
1 parent 58bdb41 commit 20b3dca

File tree

1 file changed

+121
-10
lines changed

1 file changed

+121
-10
lines changed

projects/packages/jetpack-mu-wpcom/webpack-plugins/codemirror-language-data-plugin.js

Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,58 @@
1+
/* eslint-disable prettier/prettier */
12
/**
23
* Webpack plugin for creating a virtual module with CodeMirror language data
34
*
45
* This plugin creates a virtual module that can be imported using:
5-
* import { extensionsToLanguages, sortedLangNames } from '@@codemirrorLanguageData@@';
6+
* import { extensionToLang, langNames } from '@@codemirrorLanguageData@@';
67
*
78
* The plugin generates the data from @codemirror/language-data at build time
89
* and makes it available as a virtual module without writing files to disk.
910
*/
1011

12+
const path = require( 'path' );
13+
1114
class CodeMirrorLanguageDataPlugin {
15+
/** @readonly */
1216
static virtualModuleName = '@@codemirrorLanguageData@@';
1317

18+
/** @type {?string} */
19+
virtualModulePath = null;
20+
21+
/**
22+
* Plugin apply method.
23+
*
24+
* @param {import('webpack').Compiler} compiler -- The Webpack compiler instance.
25+
*/
1426
apply( compiler ) {
15-
compiler.hooks.normalModuleFactory.tap( 'CodeMirrorLanguageDataPlugin', factory => {
16-
factory.hooks.beforeResolve.tap( 'CodeMirrorLanguageDataPlugin', resolveData => {
17-
const request = resolveData.request;
27+
// Create virtual file path
28+
this.virtualModulePath = path.resolve(
29+
compiler.context,
30+
this.constructor.virtualModuleName
31+
);
1832

19-
if ( request === CodeMirrorLanguageDataPlugin.virtualModuleName ) {
20-
// Generate the language data
21-
const moduleContent = this.generateModuleContent();
33+
// Hook into afterEnvironment to set up the virtual file system
34+
compiler.hooks.afterEnvironment.tap( this.constructor.name, () => {
35+
const content = this.generateModuleContent();
36+
this.writeVirtualFile(
37+
compiler.inputFileSystem,
38+
this.virtualModulePath,
39+
content
40+
);
41+
} );
2242

23-
// Create a virtual module
24-
resolveData.request = 'data:text/javascript,' + encodeURIComponent( moduleContent );
43+
// Hook into normalModuleFactory to intercept module resolution
44+
compiler.hooks.normalModuleFactory.tap( 'CodeMirrorLanguageDataPlugin', factory => {
45+
factory.hooks.beforeResolve.tap(
46+
'CodeMirrorLanguageDataPlugin',
47+
resolveData => {
48+
if (
49+
resolveData.request ===
50+
this.constructor.virtualModuleName
51+
) {
52+
resolveData.request = this.virtualModulePath;
53+
}
2554
}
26-
} );
55+
);
2756
} );
2857
}
2958

@@ -54,6 +83,88 @@ class CodeMirrorLanguageDataPlugin {
5483
return `export const extensionToLang = ${ JSON.stringify( extensionsToLanguages ) };
5584
export const langNames = ${ JSON.stringify( sortedLangNames ) };`;
5685
}
86+
87+
/**
88+
* Write the file.
89+
*
90+
* @param {import('webpack').InputFileSystem} fs - Virtual file system.
91+
* @param {string} filePath - Path.
92+
* @param {string} contents - File contents.
93+
*/
94+
writeVirtualFile( fs, filePath, contents ) {
95+
const stats = {
96+
isFile: () => true,
97+
isDirectory: () => false,
98+
isBlockDevice: () => false,
99+
isCharacterDevice: () => false,
100+
isSymbolicLink: () => false,
101+
isFIFO: () => false,
102+
isSocket: () => false,
103+
dev: 8675309,
104+
nlink: 1,
105+
uid: 501,
106+
gid: 20,
107+
rdev: 0,
108+
blksize: 4096,
109+
ino: Math.random(),
110+
mode: 33188,
111+
size: contents ? contents.length : 0,
112+
blocks: Math.floor( contents ? contents.length / 4096 : 0 ),
113+
atime: new Date(),
114+
mtime: new Date(),
115+
ctime: new Date(),
116+
birthtime: new Date(),
117+
};
118+
119+
// Store the file content
120+
const virtualData = {
121+
contents,
122+
stats,
123+
};
124+
125+
// Patch the filesystem methods
126+
const originalReadFileSync = fs.readFileSync;
127+
const originalStatSync = fs.statSync;
128+
const originalReadFile = fs.readFile;
129+
const originalStat = fs.stat;
130+
131+
// readFileSync
132+
fs.readFileSync = function ( filename, options ) {
133+
if ( filename === filePath ) {
134+
return virtualData.contents;
135+
}
136+
return originalReadFileSync.call( this, filename, options );
137+
}.bind( this );
138+
139+
// statSync
140+
fs.statSync = function ( filename, options ) {
141+
if ( filename === filePath ) {
142+
return virtualData.stats;
143+
}
144+
return originalStatSync.call( this, filename, options );
145+
}.bind( this );
146+
147+
fs.readFile = function ( filename, options, callback ) {
148+
if ( typeof options === 'function' ) {
149+
callback = options;
150+
options = undefined;
151+
}
152+
if ( filename === filePath ) {
153+
callback( null, virtualData.contents );
154+
return;
155+
}
156+
return originalReadFile.call( this, filename, options, callback );
157+
}.bind( this );
158+
159+
// stat (async)
160+
fs.stat = function ( filename, callback ) {
161+
if ( filename === filePath ) {
162+
callback( null, virtualData.stats );
163+
return;
164+
}
165+
return originalStat.call( this, filename, callback );
166+
}.bind( this );
167+
}
57168
}
58169

59170
module.exports = CodeMirrorLanguageDataPlugin;

0 commit comments

Comments
 (0)