Skip to content

Commit

Permalink
feat: infer webpack module name from comment
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Dec 24, 2023
1 parent 5303ebc commit 0a8a501
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 20 deletions.
16 changes: 16 additions & 0 deletions packages/webcrack/src/ast-utils/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ export function findPath<T extends t.Node>(
return path.find((path) => matcher.match(path.node)) as NodePath<T> | null;
}

export function anyFunctionExpression(
params?:
| m.Matcher<(t.Identifier | t.Pattern | t.RestElement)[]>
| (
| m.Matcher<t.Identifier>
| m.Matcher<t.Pattern>
| m.Matcher<t.RestElement>
)[],
body?: m.Matcher<t.BlockStatement>,
): m.Matcher<t.FunctionExpression | t.ArrowFunctionExpression> {
return m.or(
m.functionExpression(undefined, params, body),
m.arrowFunctionExpression(params, body),
);
}

/**
* Function expression matcher that captures the parameters
* and allows them to be referenced in the body.
Expand Down
134 changes: 134 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack5-dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// https://github.com/roots/acorn/blob/5b188cef18775b0eb9ff9448acd4a6ba442457bc/tests/Assets/__fixtures__/bud_single_runtime_dev/public/customizer.js

/******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({

/***/ 439:
/*!*****************************************!*\
!*** ./resources/scripts/customizer.js ***!
\*****************************************/
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {

/* provided dependency */ var $ = __webpack_require__(/*! jquery */ 567);
/* provided dependency */ var __react_refresh_utils__ = __webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */ 43);
__webpack_require__.$Refresh$.runtime = __webpack_require__(/*! ./node_modules/react-refresh/runtime.js */ 930);

/**
* This file allows you to add functionality to the Theme Customizer
* live preview. jQuery is readily available.
*
* @see https://codex.wordpress.org/Theme_Customization_API
*/

/**
* Change the blog name value.
*/
wp.customize('blogname', value => {
value.bind(to => $('.brand').text(to));
});

var $ReactRefreshModuleId$ = __webpack_require__.$Refresh$.moduleId;
var $ReactRefreshCurrentExports$ = __react_refresh_utils__.getModuleExports(
$ReactRefreshModuleId$
);

function $ReactRefreshModuleRuntime$(exports) {
if (true) {
var errorOverlay;
if (true) {
errorOverlay = false;
}
var testMode;
if (typeof __react_refresh_test__ !== 'undefined') {
testMode = __react_refresh_test__;
}
return __react_refresh_utils__.executeRuntime(
exports,
$ReactRefreshModuleId$,
module.hot,
errorOverlay,
testMode
);
}
}

if (typeof Promise !== 'undefined' && $ReactRefreshCurrentExports$ instanceof Promise) {
$ReactRefreshCurrentExports$.then($ReactRefreshModuleRuntime$);
} else {
$ReactRefreshModuleRuntime$($ReactRefreshCurrentExports$);
}

/***/ }),

/***/ 567:
/*!*************************!*\
!*** external "jQuery" ***!
\*************************/
/***/ (function(module) {

"use strict";
module.exports = window["jQuery"];

/***/ })

/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ if (cachedModule.error !== undefined) throw cachedModule.error;
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ loaded: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ try {
/******/ var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ };
/******/ __webpack_require__.i.forEach(function(handler) { handler(execOptions); });
/******/ module = execOptions.module;
/******/ execOptions.factory.call(module.exports, module, module.exports, execOptions.require);
/******/ } catch(e) {
/******/ module.error = e;
/******/ throw e;
/******/ }
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = __webpack_module_cache__;
/******/
/******/ // expose the module execution interceptor
/******/ __webpack_require__.i = [];
/******/
/************************************************************************/
/******/ // rest of the code removed to save space
/************************************************************************/
/******/
/******/ // module cache are used so entry inlining is disabled
/******/ // startup
/******/ // Load entry module and return exports
/******/ __webpack_require__.O(undefined, ["bud","vendor-app~editor~customizer"], function() { return __webpack_require__(9278); })
/******/ __webpack_require__.O(undefined, ["bud","vendor-app~editor~customizer"], function() { return __webpack_require__(439); })
/******/ var __webpack_exports__ = __webpack_require__.O(undefined, ["bud","vendor-app~editor~customizer"], function() { return __webpack_require__(2362); })
/******/ __webpack_exports__ = __webpack_require__.O(__webpack_exports__);
/******/
/******/ })()
;
61 changes: 61 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack5-dev.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
WebpackBundle {
"entryId": "",
"modules": Map {
"439" => WebpackModule {
"ast": /* provided dependency */var $ = require( /*! jquery */"../../jQuery");
/* provided dependency */
var __react_refresh_utils__ = require(
/*webcrack:missing*/
/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */
"../../43.js");
require.$Refresh$.runtime = require(
/*webcrack:missing*/
/*! ./node_modules/react-refresh/runtime.js */
"../../930.js");

/**
* This file allows you to add functionality to the Theme Customizer
* live preview. jQuery is readily available.
*
* @see https://codex.wordpress.org/Theme_Customization_API
*/

/**
* Change the blog name value.
*/
wp.customize('blogname', value => {
value.bind(to => $('.brand').text(to));
});
var $ReactRefreshModuleId$ = require.$Refresh$.moduleId;
var $ReactRefreshCurrentExports$ = __react_refresh_utils__.getModuleExports($ReactRefreshModuleId$);
function $ReactRefreshModuleRuntime$(exports) {
if (true) {
var errorOverlay;
if (true) {
errorOverlay = false;
}
var testMode;
if (typeof __react_refresh_test__ !== 'undefined') {
testMode = __react_refresh_test__;
}
return __react_refresh_utils__.executeRuntime(exports, $ReactRefreshModuleId$, module.hot, errorOverlay, testMode);
}
}
if (typeof Promise !== 'undefined' && $ReactRefreshCurrentExports$ instanceof Promise) {
$ReactRefreshCurrentExports$.then($ReactRefreshModuleRuntime$);
} else {
$ReactRefreshModuleRuntime$($ReactRefreshCurrentExports$);
},
"id": "439",
"isEntry": false,
"path": "./resources/scripts/customizer.js",
},
"567" => WebpackModule {
"ast": module.exports = window["jQuery"];,
"id": "567",
"isEntry": false,
"path": "jQuery",
},
},
"type": "webpack",
}
56 changes: 36 additions & 20 deletions packages/webcrack/src/unpack/webpack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import {
Transform,
anyFunctionExpression,
constKey,
constMemberExpression,
getPropName,
Expand All @@ -23,18 +24,14 @@ export const unpackWebpack = {
const moduleFunctionsMatcher = m.capture(
m.or(
// E.g. [,,function (e, t, i) {...}, ...], index is the module ID
m.arrayExpression(
m.arrayOf(
m.or(m.functionExpression(), m.arrowFunctionExpression(), null),
),
),
m.arrayExpression(m.arrayOf(m.or(anyFunctionExpression(), null))),
// E.g. {0: function (e, t, i) {...}, ...}, key is the module ID
m.objectExpression(
m.arrayOf(
m.or(
m.objectProperty(
m.or(m.numericLiteral(), m.stringLiteral(), m.identifier()),
m.or(m.functionExpression(), m.arrowFunctionExpression()),
anyFunctionExpression(),
),
// __webpack_public_path__ (c: "")
m.objectProperty(constKey('c'), m.stringLiteral()),
Expand All @@ -45,11 +42,10 @@ export const unpackWebpack = {
);

const webpack4Matcher = m.callExpression(
m.functionExpression(
undefined,
anyFunctionExpression(
undefined,
m.blockStatement(
m.anyList<t.Statement>(
m.anyList(
m.zeroOrMore(),
m.functionDeclaration(),
m.zeroOrMore(),
Expand All @@ -72,7 +68,7 @@ export const unpackWebpack = {
);

const webpack5Matcher = m.callExpression(
m.arrowFunctionExpression(
anyFunctionExpression(
undefined,
m.blockStatement(
m.anyList<t.Statement>(
Expand Down Expand Up @@ -103,12 +99,19 @@ export const unpackWebpack = {
),
),
m.zeroOrMore(),
// module.exports = entryModule
m.expressionStatement(
m.assignmentExpression(
'=',
constMemberExpression(m.identifier(), 'exports'),
m.identifier(),
m.or(
// module.exports = entryModule
m.assignmentExpression(
'=',
constMemberExpression(m.identifier(), 'exports'),
m.identifier(),
),
m.assignmentExpression(
'=',
m.identifier(),
m.callExpression(constMemberExpression(m.identifier(), 'O')),
),
),
),
),
Expand Down Expand Up @@ -194,11 +197,16 @@ export const unpackWebpack = {
lastNode.trailingComments = null;
}

const module = new WebpackModule(
moduleId,
file,
moduleId === entryIdMatcher.current?.value.toString(),
);
const isEntry =
moduleId === entryIdMatcher.current?.value.toString();
const module = new WebpackModule(moduleId, file, isEntry);

// webpack development builds include the original module name in a comment
const nameComment = moduleWrapper.node.leadingComments?.[0]?.value;
const name = nameComment && inferModuleName(nameComment);
if (name) {
module.path = name;
}

modules.set(moduleId, module);
}
Expand All @@ -212,3 +220,11 @@ export const unpackWebpack = {
};
},
} satisfies Transform<{ bundle: Bundle | undefined }>;

function inferModuleName(comment: string): string | undefined {
const match = comment.match(
/^!\*+!\*\\\n\s*!\*{3} (?:(?:external "(.+)")?|(\..+\..+)) \*{3}!/,
);
if (!match) return;
return match[1] ?? match[2];
}

0 comments on commit 0a8a501

Please sign in to comment.