Skip to content

Commit

Permalink
✨ 🔧 📝 🐛
Browse files Browse the repository at this point in the history
- added support for Webpack 5 bundles
- improved error logging with stack traces
- support to specify a custom entrypoint
- fix types not being exported properly
  • Loading branch information
pixeldesu committed Jul 25, 2021
1 parent 3cb3d1d commit a4b405b
Show file tree
Hide file tree
Showing 5 changed files with 354 additions and 257 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"types": "dist/index.d.ts",
"scripts": {
"build": "microbundle",
"postbuild": "microbundle -i src/index.ts -o dist/moduleraid.iife.js --external none --no-pkg-main -f iife",
"postbuild": "microbundle -i src/index.ts -o dist/moduleraid.iife.js --name ModuleRaid --no-pkg-main -f iife",
"dev": "microbundle watch",
"fix": "eslint --fix . --ext .js,.ts",
"lint": "eslint . --ext .js,.ts",
Expand Down
251 changes: 2 additions & 249 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,249 +1,2 @@
import {
AnyFunction,
ConstructorModuleTuple,
ModuleLike,
ModuleList,
WebpackArgument,
WebpackRequire,
WebpackRequireFunction,
} from './types'

declare function webpackJsonp(...args: WebpackArgument): ModuleLike

/**
* Main moduleRaid class
*/
export default class ModuleRaid {
/**
* Option enabling or disabling debug output
*/
private debug: boolean

/**
* A random generated module ID we use for injecting into Webpack
*/
private moduleID: string = Math.random().toString(36).substring(7)

/**
* An array containing different argument injection methods for
* Webpack, and subsequently pulling out methods and modules
*/
private arguments: WebpackArgument[] = [
[
[0],
[
(_e: unknown, _t: unknown, i: WebpackRequire) => {
const mCac = i.c
Object.keys(mCac).forEach((mod) => {
this.modules[mod] = mCac[mod].exports
})
this.constructors = i.m
this.get = i as unknown as WebpackRequireFunction
},
],
],
[
[1e3],
{
[this.moduleID]: (_e: unknown, _t: unknown, i: WebpackRequire) => {
const mCac = i.c
Object.keys(mCac).forEach((mod: string) => {
this.modules[mod] = mCac[mod].exports
})
this.constructors = i.m
this.get = i as unknown as WebpackRequireFunction
},
},
[[this.moduleID]],
],
]

/**
* Storage for the modules we extracted from Webpack
*/
public modules: ModuleList = {}

/**
* Storage for the constructors we extracted from Webpack
*/
public constructors: AnyFunction[] = []

/**
* Intermediary storage for __webpack_require__ if we were able to extract it
*/
public get: WebpackRequireFunction | null = null

/**
* moduleRaid constructor
*
* @param debug Enables debugging output for the created instance
*/
constructor(debug = false) {
this.debug = debug

this.fillModules()
this.replaceGet()
}

/**
* Debug logging method, works when true is passed as an argument to the main
* moduleRaid function, or when window.mRdebug is set to true
*
* @param {*} message The message to be logged
*/
private log(message: string): void {
if (this.debug) {
console.warn(`[moduleRaid] ${message}`)
}
}

/**
* Method to set an alternative getter if we weren't able to extract __webpack_require__
* from Webpack
*/
private replaceGet(): void {
if (this.get === null) {
this.get = (key: string | number) => this.modules[key]
}
}

/**
* Method that will try to inject a module into Webpack or get modules
* depending on it's success it might be more or less brute about it
*/
private fillModules(): void {
if (typeof webpackJsonp === 'function') {
this.arguments.forEach((argument, index) => {
try {
webpackJsonp(...argument)
} catch (err) {
this.log(`moduleRaid.args[${index}] failed: ${err}`)
}
})
} else {
try {
;(webpackJsonp as unknown as ModuleLike[]).push(this.arguments[1])
} catch (err) {
this.log(`Pushing moduleRaid.args[1] into webpackJsonp failed: ${err}`)
}
}

if (this.modules.length == 0) {
let moduleEnd = false
let moduleIterator = 0

if (!webpackJsonp([], [], [moduleIterator])) {
throw Error('Unknown Webpack structure')
}

while (!moduleEnd) {
try {
this.modules[moduleIterator] = webpackJsonp([], [], [moduleIterator])
moduleIterator++
} catch (err) {
moduleEnd = true
}
}
}
}

/**
* Method to search through the module object, searching for the fitting content
* if a string is supplied
*
* If query is supplied as a function, everything that returns true when passed
* to the query function will be returned
*
* @param query query to search the module list for
* @return a list of modules fitting the query
*/
public findModule(query: string | ((query: ModuleLike) => boolean)): ModuleLike[] {
const results: ModuleLike[] = []
const modules = Object.keys(this.modules)

if (modules.length === 0) {
throw new Error('There are no modules to search through!')
}

modules.forEach((key: string) => {
const module = this.modules[key]

try {
if (typeof query === 'string') {
query = query.toLowerCase()

switch (typeof module) {
case 'string':
if (module.includes(query)) results.push(module)
break
case 'function':
if (module.toString().toLowerCase().includes(query)) results.push(module)
break
case 'object':
if (typeof (module as { default: {}}).default === 'object') {
for (key in (module as { default: {}}).default) {
if (key.toLowerCase() === query) results.push(module);
}
}

for (key in module) {
if (key.toLowerCase() === query) results.push(module);
}
break
}
} else if (typeof query === 'function') {
if (query(module)) results.push(module)
} else {
throw new TypeError(
`findModule can only find via string and function, ${typeof query} was passed`
)
}
} catch (e) {
this.log(`There was an error while searching through module '${key}'\n${e}`)
}
})

return results
}

/**
* Method to search through the constructor array, searching for the fitting content
* if a string is supplied
*
* If query is supplied as a function, everything that returns true when passed
* to the query function will be returned
*
* @param query query to search the constructor list for
* @returns a list of constructor/module tuples fitting the query
*/
public findConstructor(
query: string | ((query: ModuleLike) => boolean)
): ConstructorModuleTuple[] {
const results: ConstructorModuleTuple[] = []
const constructors = Object.keys(this.constructors)

if (constructors.length === 0) {
throw new Error('There are no constructors to search through!')
}

constructors.forEach((key: string) => {
const constructor = this.constructors[key as unknown as number]

try {
if (typeof query === 'string') {
query = query.toLowerCase()

if (constructor.toString().toLowerCase().includes(query))
results.push([this.constructors[key as unknown as number], this.modules[key]])
} else if (typeof query === 'function') {
if (query(constructor))
results.push([this.constructors[key as unknown as number], this.modules[key]])
}
} catch (e) {
this.log(`There was an error while searching through constructor '${key}'\n${e}`)
}
})

return results
}
}
export * from './types'
export { ModuleRaid as default } from './moduleraid'
Loading

0 comments on commit a4b405b

Please sign in to comment.