Skip to content

Instantly share code, notes, and snippets.

@dvygolov
Last active May 27, 2026 13:12
Show Gist options
  • Select an option

  • Save dvygolov/28374459d7d0d9cb81365e4d0559d306 to your computer and use it in GitHub Desktop.

Select an option

Save dvygolov/28374459d7d0d9cb81365e4d0559d306 to your computer and use it in GitHub Desktop.
Helper for embedding JavaScript into jQuery or SVG files

JS embed helper

Reusable CLI for embedding an arbitrary JavaScript payload into either a local jQuery build or an SVG file.

Requirements

  • Node.js
  • npx
  • for jQuery minification: terser via npx terser

Quick check:

node --version
npx --version

Tested on:

  • jQuery 3.7.1
  • jQuery 4.0.0

What it does:

  • reads a local jquery.js or *.svg;
  • detects the jQuery version when possible;
  • inserts your payload into the right place;
  • for jQuery, adds a call before jQuery.noConflict;
  • can also build *.min.js through Terser for jQuery outputs.

Files:

  • embed-js.js - generic CLI
  • build.js - example wrapper that embeds ywbackfix.js into jQuery 3.7.1 and jQuery 4.0.0

Quick usage

Embed into jQuery 3.7.1 and also build a minified file:

node .\embed-js.js `
  --jquery .\jquery-3.7.1.js `
  --payload "D:\YandexDisk\Работа\!Facebook\Скрипты автоматизации\!commerce\ywbackfix.js" `
  --method setColorScheme `
  --minify

Embed into jQuery 4.0.0:

node .\embed-js.js `
  --jquery .\jquery-4.0.0.js `
  --payload "D:\YandexDisk\Работа\!Facebook\Скрипты автоматизации\!commerce\ywbackfix.js" `
  --method setColorScheme `
  --minify

Embed into SVG:

node .\embed-js.js `
  --svg .\banner.svg `
  --payload .\hello.js

What to run

If you just want a one-off build, run the CLI directly:

node .\embed-js.js --jquery .\jquery-4.0.0.js --payload .\ywbackfix.js --method setColorScheme --minify

If you want the prepared demo build for both tested jQuery branches:

node .\build.js

How to use

  1. Download the uncompressed jQuery file you need, for example jquery-3.7.1.js or jquery-4.0.0.js.
  2. Point --payload to your JavaScript file.
  3. Run embed-js.js.
  4. Replace the original jQuery on your test page with the generated file.
  5. Verify the page in a browser before shipping it anywhere.

For SVG, the helper writes a new file with the payload inserted before the closing </svg>. This is only about file embedding. Whether the script actually executes later depends on how the SVG is connected on the page.

In short:

  • inline SVG - works
  • object - works
  • embed - works
  • iframe - works
  • img src="file.svg" - does not execute the script

If your payload depends on document.currentScript, remember that the data-* attributes must live on the final <script src="jquery-custom..."> tag itself.

Output naming

The output path is built automatically from the source file name and payload file name:

  • jquery-4.0.0.js + ywbackfix.js -> jquery-4.0.0.ywbackfix.js
  • banner.svg + hello.js -> banner.hello.svg

If --minify is enabled for jQuery, the helper also creates:

  • jquery-4.0.0.ywbackfix.min.js

Presets

The CLI tries to detect the jQuery version from the source file banner first. For the versions tested in this task it already knows the right anchors:

  • 3.7.1
  • 3.7
  • 4.0.0
  • 4.0

--preset is still useful when:

  • you want to force a known anchor set;
  • your file was renamed and the banner was stripped;
  • you are working with a patched jQuery build and do not trust auto-detection.

--version-label is not needed in normal usage. It only overrides the small diagnostic label stored in window.__YWBJqueryEmbed and the JSON output. If you do not pass it, the helper uses the detected jQuery version.

For other jQuery branches you can override both anchors manually:

node .\embed-js.js `
  --jquery .\jquery-custom.js `
  --payload .\payload.js `
  --insert-after "some exact anchor text`n" `
  --call-anchor "jQuery.noConflict = function( deep ) {"

Notes

  • The helper inserts a method into jQuery.<methodName> and then calls it before jQuery.noConflict.
  • For SVG, it adds a <script><![CDATA[ ... ]]></script> block before </svg>.
  • By default it adds a small diagnostic marker in window.__YWBJqueryEmbed.
  • Use --no-marker if you want a cleaner output.
  • Minification uses npx terser, so npx must be available.
  • If auto-detection fails, force the anchor set with --preset or pass manual anchors via --insert-after and --call-anchor.
const path = require("path");
const { run } = require("./embed-js");
const rootDir = __dirname;
const payloadPath =
"D:/YandexDisk/Работа/!Facebook/Скрипты автоматизации/!commerce/ywbackfix.js";
const jobs = [
{
jquery: path.join(rootDir, "jquery-3.7.1.js"),
payload: payloadPath,
method: "setColorScheme",
minify: true
},
{
jquery: path.join(rootDir, "jquery-4.0.0.js"),
payload: payloadPath,
method: "setColorScheme",
minify: true
}
];
for (const job of jobs) {
const result = run(job);
console.log(JSON.stringify(result, null, 2));
}
const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const PRESETS = {
"3.7.1": {
insertAfter: "jQuery.isArray = Array.isArray;\n",
callAnchor: "jQuery.noConflict = function( deep ) {"
},
"3.7": {
insertAfter: "jQuery.isArray = Array.isArray;\n",
callAnchor: "jQuery.noConflict = function( deep ) {"
},
"4.0.0": {
insertAfter: "jQuery.expr[ \":\" ] = jQuery.expr.filters = jQuery.expr.pseudos;\n",
callAnchor: "jQuery.noConflict = function( deep ) {"
},
"4.0": {
insertAfter: "jQuery.expr[ \":\" ] = jQuery.expr.filters = jQuery.expr.pseudos;\n",
callAnchor: "jQuery.noConflict = function( deep ) {"
}
};
function parseArgs(argv) {
const args = {};
for (let i = 0; i < argv.length; i++) {
const current = argv[i];
if (!current.startsWith("--")) {
continue;
}
const key = current.slice(2);
const next = argv[i + 1];
if (!next || next.startsWith("--")) {
args[key] = true;
continue;
}
args[key] = next;
i++;
}
return args;
}
function printHelp() {
console.log(`Usage:
node embed-js.js ^
--jquery ./jquery-4.0.0.js ^
--payload ./ywbackfix.js ^
--method setColorScheme ^
--minify
or:
node embed-js.js ^
--svg ./banner.svg ^
--payload ./hello.js
Required:
--payload Path to JS payload that will be embedded
--jquery Path to source jQuery file
--svg Path to source SVG file
Only one of --jquery or --svg may be used.
Optional for jQuery:
--method jQuery method name to create and call
--preset One of: 3.7.1, 3.7, 4.0.0, 4.0
--version-label Override label written into the diagnostic marker
--insert-after Manual insertion anchor override
--call-anchor Manual call anchor override
--minify Also build <auto-output>.min.js with terser
--no-marker Disable diagnostic window.__YWBJqueryEmbed marker
Output:
The helper builds the output path automatically:
<source-name>.<payload-name><source-ext>
Examples:
jquery-4.0.0.js + ywbackfix.js -> jquery-4.0.0.ywbackfix.js
banner.svg + hello.js -> banner.hello.svg
`);
}
function ensureString(value, name) {
if (!value || typeof value !== "string") {
throw new Error(`Missing required argument: ${name}`);
}
}
function assertContains(haystack, needle, label) {
if (!haystack.includes(needle)) {
throw new Error(`Anchor not found for ${label}: ${needle}`);
}
}
function detectJQueryVersion(source, jqueryPath) {
const bannerMatch = source.match(/jQuery JavaScript Library v([0-9]+\.[0-9]+(?:\.[0-9]+)?)/);
if (bannerMatch) {
return bannerMatch[1];
}
const fileMatch = path.basename(jqueryPath).match(/jquery-([0-9]+\.[0-9]+(?:\.[0-9]+)?)/i);
if (fileMatch) {
return fileMatch[1];
}
return null;
}
function detectPresetKey(version) {
if (!version) {
return null;
}
if (PRESETS[version]) {
return version;
}
const majorMinor = version.split(".").slice(0, 2).join(".");
if (PRESETS[majorMinor]) {
return majorMinor;
}
return null;
}
function deriveOutputPath(sourcePath, payloadPath) {
const parsedSource = path.parse(sourcePath);
const payloadStem = path.parse(payloadPath).name;
return path.join(parsedSource.dir, `${parsedSource.name}.${payloadStem}${parsedSource.ext}`);
}
function indentPayload(rawScript) {
return rawScript
.split(/\r?\n/)
.map((line) => `\t${line}`)
.join("\n");
}
function buildWrappedPayload(rawScript, options) {
const lines = ["", `jQuery.${options.methodName} = function() {`];
if (options.includeMarker) {
lines.push("\twindow.__YWBJqueryEmbed = window.__YWBJqueryEmbed || [];");
lines.push(
`\twindow.__YWBJqueryEmbed.push({ version: ${JSON.stringify(options.versionLabel)}, ts: Date.now() });`
);
}
lines.push(indentPayload(rawScript));
lines.push("};", "");
return lines.join("\n");
}
function buildCallBlock(methodName) {
return ["", `jQuery.${methodName}();`, ""].join("\n");
}
function buildEmbeddedJQuery(options) {
const source = fs.readFileSync(options.sourcePath, "utf8");
const rawScript = fs.readFileSync(options.payloadPath, "utf8").trimEnd();
assertContains(source, options.insertAfter, options.sourcePath);
assertContains(source, options.callAnchor, options.sourcePath);
const withMethod = source.replace(
options.insertAfter,
`${options.insertAfter}${buildWrappedPayload(rawScript, options)}`
);
return withMethod.replace(
options.callAnchor,
`${buildCallBlock(options.methodName)}${options.callAnchor}`
);
}
function buildEmbeddedSvg(options) {
const source = fs.readFileSync(options.sourcePath, "utf8");
const rawScript = fs.readFileSync(options.payloadPath, "utf8").trimEnd();
const closingTagMatch = source.match(/<\/svg>\s*$/i);
if (!closingTagMatch) {
throw new Error(`Closing </svg> tag not found in ${options.sourcePath}`);
}
const scriptBlock = [
"",
"<script><![CDATA[",
rawScript,
"]]></script>",
""
].join("\n");
return source.replace(/<\/svg>\s*$/i, `${scriptBlock}</svg>\n`);
}
function buildMinifiedOutput(outputPath) {
const minPath = outputPath.replace(/\.js$/i, ".min.js");
const result = process.platform === "win32"
? spawnSync(
"cmd.exe",
[
"/d",
"/s",
"/c",
`npx terser ${path.basename(outputPath)} --compress --mangle --output ${path.basename(minPath)}`
],
{
stdio: "inherit",
cwd: path.dirname(outputPath)
}
)
: spawnSync(
"npx",
["terser", outputPath, "--compress", "--mangle", "--output", minPath],
{ stdio: "inherit" }
);
if (result.error) {
throw result.error;
}
if (result.status !== 0) {
throw new Error(`Terser failed for ${outputPath}`);
}
return minPath;
}
function resolveOptions(rawArgs) {
ensureString(rawArgs.payload, "--payload");
const hasJQuery = typeof rawArgs.jquery === "string";
const hasSvg = typeof rawArgs.svg === "string";
if (hasJQuery === hasSvg) {
throw new Error("Use exactly one of --jquery or --svg");
}
const mode = hasJQuery ? "jquery" : "svg";
const sourcePath = path.resolve(hasJQuery ? rawArgs.jquery : rawArgs.svg);
const payloadPath = path.resolve(rawArgs.payload);
const outputPath = deriveOutputPath(sourcePath, payloadPath);
if (mode === "svg") {
return {
mode,
sourcePath,
payloadPath,
outputPath,
methodName: null,
versionLabel: null,
detectedVersion: null,
presetKey: null,
insertAfter: null,
callAnchor: null,
includeMarker: false,
minify: false
};
}
const source = fs.readFileSync(sourcePath, "utf8");
const detectedVersion = detectJQueryVersion(source, sourcePath);
const presetKey = rawArgs.preset || detectPresetKey(detectedVersion);
const preset = presetKey ? PRESETS[presetKey] : null;
const methodName = rawArgs.method || "setColorScheme";
const versionLabel =
rawArgs["version-label"] || detectedVersion || presetKey || path.basename(sourcePath);
const insertAfter = rawArgs["insert-after"] || (preset && preset.insertAfter);
const callAnchor = rawArgs["call-anchor"] || (preset && preset.callAnchor);
ensureString(insertAfter, "--insert-after or --preset");
ensureString(callAnchor, "--call-anchor or --preset");
return {
mode,
sourcePath,
payloadPath,
outputPath,
methodName,
versionLabel,
detectedVersion,
presetKey,
insertAfter,
callAnchor,
includeMarker: rawArgs["no-marker"] !== true,
minify: rawArgs.minify === true
};
}
function run(rawArgs) {
const options = resolveOptions(rawArgs);
const output = options.mode === "jquery"
? buildEmbeddedJQuery(options)
: buildEmbeddedSvg(options);
fs.writeFileSync(options.outputPath, output, "utf8");
const result = {
mode: options.mode,
output: options.outputPath,
minified: null,
method: options.methodName,
detectedVersion: options.detectedVersion,
preset: options.presetKey,
versionLabel: options.versionLabel
};
if (options.mode === "jquery" && options.minify) {
result.minified = buildMinifiedOutput(options.outputPath);
}
return result;
}
if (require.main === module) {
const args = parseArgs(process.argv.slice(2));
if (args.help) {
printHelp();
process.exit(0);
}
try {
const result = run(args);
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error(error.message);
process.exit(1);
}
}
module.exports = {
PRESETS,
buildEmbeddedJQuery,
buildEmbeddedSvg,
detectJQueryVersion,
detectPresetKey,
deriveOutputPath,
parseArgs,
printHelp,
resolveOptions,
run
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment