simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

CompatibilityPlugin.js (4645B)


      1 /*
      2 	MIT License http://www.opensource.org/licenses/mit-license.php
      3 	Author Tobias Koppers @sokra
      4 */
      5 
      6 "use strict";
      7 
      8 const ConstDependency = require("./dependencies/ConstDependency");
      9 
     10 /** @typedef {import("./Compiler")} Compiler */
     11 /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
     12 
     13 const nestedWebpackRequireTag = Symbol("nested __webpack_require__");
     14 
     15 class CompatibilityPlugin {
     16 	/**
     17 	 * Apply the plugin
     18 	 * @param {Compiler} compiler the compiler instance
     19 	 * @returns {void}
     20 	 */
     21 	apply(compiler) {
     22 		compiler.hooks.compilation.tap(
     23 			"CompatibilityPlugin",
     24 			(compilation, { normalModuleFactory }) => {
     25 				compilation.dependencyTemplates.set(
     26 					ConstDependency,
     27 					new ConstDependency.Template()
     28 				);
     29 
     30 				normalModuleFactory.hooks.parser
     31 					.for("javascript/auto")
     32 					.tap("CompatibilityPlugin", (parser, parserOptions) => {
     33 						if (
     34 							parserOptions.browserify !== undefined &&
     35 							!parserOptions.browserify
     36 						)
     37 							return;
     38 
     39 						parser.hooks.call
     40 							.for("require")
     41 							.tap("CompatibilityPlugin", expr => {
     42 								// support for browserify style require delegator: "require(o, !0)"
     43 								if (expr.arguments.length !== 2) return;
     44 								const second = parser.evaluateExpression(expr.arguments[1]);
     45 								if (!second.isBoolean()) return;
     46 								if (second.asBool() !== true) return;
     47 								const dep = new ConstDependency("require", expr.callee.range);
     48 								dep.loc = expr.loc;
     49 								if (parser.state.current.dependencies.length > 0) {
     50 									const last =
     51 										parser.state.current.dependencies[
     52 											parser.state.current.dependencies.length - 1
     53 										];
     54 									if (
     55 										last.critical &&
     56 										last.options &&
     57 										last.options.request === "." &&
     58 										last.userRequest === "." &&
     59 										last.options.recursive
     60 									)
     61 										parser.state.current.dependencies.pop();
     62 								}
     63 								parser.state.module.addPresentationalDependency(dep);
     64 								return true;
     65 							});
     66 					});
     67 
     68 				/**
     69 				 * @param {JavascriptParser} parser the parser
     70 				 * @returns {void}
     71 				 */
     72 				const handler = parser => {
     73 					// Handle nested requires
     74 					parser.hooks.preStatement.tap("CompatibilityPlugin", statement => {
     75 						if (
     76 							statement.type === "FunctionDeclaration" &&
     77 							statement.id &&
     78 							statement.id.name === "__webpack_require__"
     79 						) {
     80 							const newName = `__nested_webpack_require_${statement.range[0]}__`;
     81 							parser.tagVariable(statement.id.name, nestedWebpackRequireTag, {
     82 								name: newName,
     83 								declaration: {
     84 									updated: false,
     85 									loc: statement.id.loc,
     86 									range: statement.id.range
     87 								}
     88 							});
     89 							return true;
     90 						}
     91 					});
     92 					parser.hooks.pattern
     93 						.for("__webpack_require__")
     94 						.tap("CompatibilityPlugin", pattern => {
     95 							const newName = `__nested_webpack_require_${pattern.range[0]}__`;
     96 							parser.tagVariable(pattern.name, nestedWebpackRequireTag, {
     97 								name: newName,
     98 								declaration: {
     99 									updated: false,
    100 									loc: pattern.loc,
    101 									range: pattern.range
    102 								}
    103 							});
    104 							return true;
    105 						});
    106 					parser.hooks.expression
    107 						.for(nestedWebpackRequireTag)
    108 						.tap("CompatibilityPlugin", expr => {
    109 							const { name, declaration } = parser.currentTagData;
    110 							if (!declaration.updated) {
    111 								const dep = new ConstDependency(name, declaration.range);
    112 								dep.loc = declaration.loc;
    113 								parser.state.module.addPresentationalDependency(dep);
    114 								declaration.updated = true;
    115 							}
    116 							const dep = new ConstDependency(name, expr.range);
    117 							dep.loc = expr.loc;
    118 							parser.state.module.addPresentationalDependency(dep);
    119 							return true;
    120 						});
    121 
    122 					// Handle hashbang
    123 					parser.hooks.program.tap(
    124 						"CompatibilityPlugin",
    125 						(program, comments) => {
    126 							if (comments.length === 0) return;
    127 							const c = comments[0];
    128 							if (c.type === "Line" && c.range[0] === 0) {
    129 								if (parser.state.source.slice(0, 2).toString() !== "#!") return;
    130 								// this is a hashbang comment
    131 								const dep = new ConstDependency("//", 0);
    132 								dep.loc = c.loc;
    133 								parser.state.module.addPresentationalDependency(dep);
    134 							}
    135 						}
    136 					);
    137 				};
    138 
    139 				normalModuleFactory.hooks.parser
    140 					.for("javascript/auto")
    141 					.tap("CompatibilityPlugin", handler);
    142 				normalModuleFactory.hooks.parser
    143 					.for("javascript/dynamic")
    144 					.tap("CompatibilityPlugin", handler);
    145 				normalModuleFactory.hooks.parser
    146 					.for("javascript/esm")
    147 					.tap("CompatibilityPlugin", handler);
    148 			}
    149 		);
    150 	}
    151 }
    152 module.exports = CompatibilityPlugin;