Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

js-inline-loader

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

js-inline-loader - npm Package Compare versions

Comparing version
0.1.1
to
0.2.0
+137
-37
js-inline-loader.js

@@ -0,7 +1,7 @@

const esprima = require('esprima');
const escodegen = require('escodegen');
const fs = require("fs");
const path = require("path");
const esprima = require('esprima');
const escodegen = require('escodegen');
const INLINE_MACRO = /^([\t ]*)(\S.*)?%inline\s*\(\s*['"]([^"']+)["']\s*\)\s*\.(\w+)\s*\(([\s\S]*?)\);$/gm;
const MACRO = /%inline\s*\(\s*["'](.*?)['"]\s*\)\s*\.\s*(\w+)\s*\((\s*\))?/g;

@@ -163,2 +163,7 @@ /**

// Empty functions have empty contents
if (ast.body.body.length === 0) {
return '';
}
// Functions that have only a return statement, skip the 'return' and

@@ -292,49 +297,144 @@ // render only it's value

/**
* Export the webpack replace function
* Return the inline contents of the given function from the given module with
* the given argument string.
*
* @this {loaderAPI} The function should be bound to the webpack loader API
* @param {String} gModule - The module that contains the actions
* @param {String} gFunction - The module function to inline
* @param {String} gArgs - The arguments passed to the inline function
* @returns {String} Returns the generated code for this inline function
*/
module.exports = function(source) {
this.cacheable();
function getFunctionCode(gModule, gFunction, fnArgs) {
// Resolve filename
var filePath = path.resolve(this.context, gModule);
var contents = loadModuleContents(filePath, this.options);
if (!contents) {
this.emitError('Could not find module `' + gModule + '`');
return '/* Missing module ' + gModule + ' */';
}
// Replace all the %inline macro encounters
return source.replace(INLINE_MACRO, (function(m, gIndent, gAssignExpr, gFile, gFunction, gArgs) {
// Load file and extract AST
var ast;
try {
ast = esprima.parse(contents, { sourceType: 'module' })
} catch (e) {
this.emitError('%inline("' + gModule + '"): ' + e.toString());
return '/* Parsing error in module ' + gModule + ' */';
}
this.addDependency(filePath);
// Resolve filename
var filePath = path.resolve(this.context, gFile);
// Locate the correct function AST
var exportedFn = getExportedFunctions(ast);
var fnAst = exportedFn[gFunction];
if (!fnAst) {
this.emitError('%inline("' + gModule + '"): Undefined function `' + gFunction);
return '/* Unknown inline ' + gFunction + ' */';
}
// Load file and extract AST
var ast = esprima.parse(loadModuleContents(filePath, this.options), { sourceType: 'module' })
this.addDependency(filePath);
// Replace arguments in the function ast
for (var i=0; i<fnArgs.length; ++i) {
fnAst = replaceIdentifier( fnAst.params[i].name, fnArgs[i], fnAst );
}
// Locate the correct function AST
var exportedFn = getExportedFunctions(ast);
var fnAst = exportedFn[gFunction];
if (!fnAst) {
this.emitError('Trying to inline unknown function ' + gFunction + ' in module ' + gFile);
return '/* Unknown inline ' + gFunction + ' */';
}
// Render contents
return renderFunction(fnAst);
}
// Compile the arguments ast
var fnArgs = compileArgumentAst(gArgs);
if (fnArgs.length !== fnAst.params.length) {
this.emitError('Function ' + gFunction + ' is expecting exactly ' +
fnAst.params.length + ' arguments, but got ' + fnArgs.length);
return '/* Invalid syntax for ' + gFunction + ' */';
/**
* This function walks the AST and returns the node that calls the magic
* inline function `___js_inline_loader_inline`.
*
* @param {Object} ast - The AST to walk
* @returns {Object} Returns the AST node of the magic function
*/
function findInlineToken(ast) {
if ((ast.type === 'CallExpression') && (ast.callee.name === '___js_inline_loader_inline')) {
return ast;
}
// Walk object properties
var keys = Object.keys(ast);
for (var i=0, l=keys.length; i<l; ++i) {
var key = keys[i];
var value = ast[key];
// Process each item of an array
if (Array.isArray(value)) {
for (var j=0, jl=value.length; j<jl; ++j) {
if (typeof value[j] === 'object') {
var ans = findInlineToken(value[j]);
if (ans) return ans;
}
}
// And forward the checks of the objects
} else if ((typeof value === 'object') && (value !== null)) {
var ans = findInlineToken(value);
if (ans) return ans;
}
}
// Replace arguments in the function ast
for (var i=0; i<fnArgs.length; ++i) {
fnAst = replaceIdentifier( fnAst.params[i].name, fnArgs[i], fnAst );
// Nothing found
return null;
}
/**
* Correct replacement of an `%inline` macro, using AST processing
*
* @param {String} source - The source code to proess
* @param {Function} callback - The callback to use to get replacements
* @returns {String} Returns the new source
*/
function replaceInlineFunc(source, callback) {
while (true) {
var ast;
// Parse the current source into the AST
try {
ast = esprima.parse(source, {sourceType: 'module', range: true});
} catch (e) {
this.emitError('Inline processing failed: SyntaxError: ' + e.toString());
return source;
}
// Compile the code and properly indent it
var code = renderFunction(fnAst);
if (gAssignExpr) {
code = gIndent + gAssignExpr + code.replace(/\r?\n/g, '\n'+gIndent+' ');
} else {
code = gIndent + code.replace(/\r?\n/g, '\n'+gIndent);
// Find an inline token
var token = findInlineToken(ast);
if (!token) {
return source;
}
return code;
}).bind(this));
// Found a token? Callback with details
var replacement = callback(
token.arguments[0].value,
token.arguments[1].name,
token.arguments.slice(2)
);
// Replace that part of the source with the generated code
source = source.substring(0, token.range[0]) + replacement +
source.substring(token.range[1]);
}
}
/**
* Export the webpack replace function
*/
module.exports = function(source) {
this.cacheable();
// Convert the convenient `%macro` to a proper JS expression
var hasMacros = false;
var normSource = source.replace(MACRO, function(m, gModule, gFunction, gEmpty) {
hasMacros = true;
return '___js_inline_loader_inline(\'' + gModule + '\',' + gFunction + (gEmpty ? ')' : ',');
});
// If we don't have macros, don't bother
if (!hasMacros) {
return source;
}
// Replace all inline functions using the AST
return replaceInlineFunc.call(this, normSource, getFunctionCode.bind(this));
};
{
"name": "js-inline-loader",
"version": "0.1.1",
"version": "0.2.0",
"description": "A webpack loader than enables inlining functions from other modules",

@@ -5,0 +5,0 @@ "main": "js-inline-loader.js",