Add semantic token highlighting for 'that' variable and comment file references in VS Code extension Add Phone_Text_Input and Currency_Input components with formatting utilities Implement client widgets, form standardization, and soft delete functionality Add modal scroll lock and update documentation Implement comprehensive modal system with form integration and validation Fix modal component instantiation using jQuery plugin API Implement modal system with responsive sizing, queuing, and validation support Implement form submission with validation, error handling, and loading states Implement country/state selectors with dynamic data loading and Bootstrap styling Revert Rsx::Route() highlighting in Blade/PHP files Target specific PHP scopes for Rsx::Route() highlighting in Blade Expand injection selector for Rsx::Route() highlighting Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls Update jqhtml packages to v2.2.165 Add bundle path validation for common mistakes (development mode only) Create Ajax_Select_Input widget and Rsx_Reference_Data controller Create Country_Select_Input widget with default country support Initialize Tom Select on Select_Input widgets Add Tom Select bundle for enhanced select dropdowns Implement ISO 3166 geographic data system for country/region selection Implement widget-based form system with disabled state support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
320 lines
8.2 KiB
JavaScript
Executable File
320 lines
8.2 KiB
JavaScript
Executable File
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Natsu @xiaoxiaojx
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const path = require("path");
|
|
const urlUtils = require("url");
|
|
const { isAbsolute, join } = require("./fs");
|
|
|
|
/** @typedef {import("./fs").InputFileSystem} InputFileSystem */
|
|
|
|
/**
|
|
* @typedef {(input: string | Buffer<ArrayBufferLike>, resourcePath: string, fs: InputFileSystem) => Promise<{source: string | Buffer<ArrayBufferLike>, sourceMap: string | RawSourceMap | undefined, fileDependencies: string[]}>} SourceMapExtractorFunction
|
|
*/
|
|
|
|
/** @typedef {import("webpack-sources").RawSourceMap} RawSourceMap */
|
|
|
|
/**
|
|
* @typedef {(resourcePath: string) => Promise<string | Buffer<ArrayBufferLike>>} ReadResource
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} SourceMappingURL
|
|
* @property {string} sourceMappingURL
|
|
* @property {string} replacementString
|
|
*/
|
|
|
|
// Matches only the last occurrence of sourceMappingURL
|
|
const innerRegex = /\s*[#@]\s*sourceMappingURL\s*=\s*([^\s'"]*)\s*/;
|
|
|
|
const validProtocolPattern = /^[a-z][a-z0-9+.-]*:/i;
|
|
|
|
const sourceMappingURLRegex = new RegExp(
|
|
"(?:" +
|
|
"/\\*" +
|
|
"(?:\\s*\r?\n(?://)?)?" +
|
|
`(?:${innerRegex.source})` +
|
|
"\\s*" +
|
|
"\\*/" +
|
|
"|" +
|
|
`//(?:${innerRegex.source})` +
|
|
")" +
|
|
"\\s*"
|
|
);
|
|
|
|
/**
|
|
* Extract source mapping URL from code comments
|
|
* @param {string} code source code content
|
|
* @returns {SourceMappingURL} source mapping information
|
|
*/
|
|
function getSourceMappingURL(code) {
|
|
const lines = code.split(/^/m);
|
|
let match;
|
|
|
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
match = lines[i].match(sourceMappingURLRegex);
|
|
if (match) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const sourceMappingURL = match ? match[1] || match[2] || "" : "";
|
|
|
|
return {
|
|
sourceMappingURL: sourceMappingURL
|
|
? decodeURI(sourceMappingURL)
|
|
: sourceMappingURL,
|
|
replacementString: match ? match[0] : ""
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get absolute path for source file
|
|
* @param {string} context context directory
|
|
* @param {string} request file request
|
|
* @param {string} sourceRoot source root directory
|
|
* @returns {string} absolute path
|
|
*/
|
|
function getAbsolutePath(context, request, sourceRoot) {
|
|
if (sourceRoot) {
|
|
if (isAbsolute(sourceRoot)) {
|
|
return join(undefined, sourceRoot, request);
|
|
}
|
|
|
|
return join(undefined, join(undefined, context, sourceRoot), request);
|
|
}
|
|
|
|
return join(undefined, context, request);
|
|
}
|
|
|
|
/**
|
|
* Check if value is a URL
|
|
* @param {string} value string to check
|
|
* @returns {boolean} true if value is a URL
|
|
*/
|
|
function isURL(value) {
|
|
return validProtocolPattern.test(value) && !path.win32.isAbsolute(value);
|
|
}
|
|
|
|
/**
|
|
* Fetch from multiple possible file paths
|
|
* @param {ReadResource} readResource read resource function
|
|
* @param {string[]} possibleRequests array of possible file paths
|
|
* @param {string} errorsAccumulator accumulated error messages
|
|
* @returns {Promise<{path: string, data?: string}>} source content promise
|
|
*/
|
|
async function fetchPathsFromURL(
|
|
readResource,
|
|
possibleRequests,
|
|
errorsAccumulator = ""
|
|
) {
|
|
let result;
|
|
|
|
try {
|
|
result = await readResource(possibleRequests[0]);
|
|
} catch (error) {
|
|
errorsAccumulator += `${/** @type {Error} */ (error).message}\n\n`;
|
|
|
|
const [, ...tailPossibleRequests] = possibleRequests;
|
|
|
|
if (tailPossibleRequests.length === 0) {
|
|
/** @type {Error} */ (error).message = errorsAccumulator;
|
|
|
|
throw error;
|
|
}
|
|
|
|
return fetchPathsFromURL(
|
|
readResource,
|
|
tailPossibleRequests,
|
|
errorsAccumulator
|
|
);
|
|
}
|
|
|
|
return {
|
|
path: possibleRequests[0],
|
|
data: result.toString("utf8")
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch source content from URL
|
|
* @param {ReadResource} readResource The read resource function
|
|
* @param {string} context context directory
|
|
* @param {string} url source URL
|
|
* @param {string=} sourceRoot source root directory
|
|
* @param {boolean=} skipReading whether to skip reading file content
|
|
* @returns {Promise<{sourceURL: string, sourceContent?: string | Buffer<ArrayBufferLike>}>} source content promise
|
|
*/
|
|
async function fetchFromURL(
|
|
readResource,
|
|
context,
|
|
url,
|
|
sourceRoot,
|
|
skipReading = false
|
|
) {
|
|
// 1. It's an absolute url and it is not `windows` path like `C:\dir\file`
|
|
if (isURL(url)) {
|
|
// eslint-disable-next-line n/no-deprecated-api
|
|
const { protocol } = urlUtils.parse(url);
|
|
if (protocol === "data:") {
|
|
const sourceContent = skipReading ? "" : await readResource(url);
|
|
|
|
return { sourceURL: "", sourceContent };
|
|
}
|
|
|
|
if (protocol === "file:") {
|
|
const pathFromURL = urlUtils.fileURLToPath(url);
|
|
const sourceURL = path.normalize(pathFromURL);
|
|
const sourceContent = skipReading ? "" : await readResource(sourceURL);
|
|
|
|
return { sourceURL, sourceContent };
|
|
}
|
|
|
|
const sourceContent = skipReading ? "" : await readResource(url);
|
|
return { sourceURL: url, sourceContent };
|
|
}
|
|
|
|
// 3. Absolute path
|
|
if (isAbsolute(url)) {
|
|
let sourceURL = path.normalize(url);
|
|
|
|
let sourceContent;
|
|
|
|
if (!skipReading) {
|
|
const possibleRequests = [sourceURL];
|
|
|
|
if (url.startsWith("/")) {
|
|
possibleRequests.push(
|
|
getAbsolutePath(context, sourceURL.slice(1), sourceRoot || "")
|
|
);
|
|
}
|
|
|
|
const result = await fetchPathsFromURL(readResource, possibleRequests);
|
|
|
|
sourceURL = result.path;
|
|
sourceContent = result.data;
|
|
}
|
|
|
|
return { sourceURL, sourceContent };
|
|
}
|
|
|
|
// 4. Relative path
|
|
const sourceURL = getAbsolutePath(context, url, sourceRoot || "");
|
|
let sourceContent;
|
|
|
|
if (!skipReading) {
|
|
sourceContent = await readResource(sourceURL);
|
|
}
|
|
|
|
return { sourceURL, sourceContent };
|
|
}
|
|
|
|
/**
|
|
* Extract source map from code content
|
|
* @param {string | Buffer<ArrayBufferLike>} stringOrBuffer The input code content as string or buffer
|
|
* @param {string} resourcePath The path to the resource file
|
|
* @param {ReadResource} readResource The read resource function
|
|
* @returns {Promise<{source: string | Buffer<ArrayBufferLike>, sourceMap: string | RawSourceMap | undefined}>} Promise resolving to extracted source map information
|
|
*/
|
|
async function extractSourceMap(stringOrBuffer, resourcePath, readResource) {
|
|
const input =
|
|
typeof stringOrBuffer === "string"
|
|
? stringOrBuffer
|
|
: stringOrBuffer.toString("utf8");
|
|
const inputSourceMap = undefined;
|
|
const output = {
|
|
source: stringOrBuffer,
|
|
sourceMap: inputSourceMap
|
|
};
|
|
const { sourceMappingURL, replacementString } = getSourceMappingURL(input);
|
|
|
|
if (!sourceMappingURL) {
|
|
return output;
|
|
}
|
|
|
|
const baseContext = path.dirname(resourcePath);
|
|
|
|
const { sourceURL, sourceContent } = await fetchFromURL(
|
|
readResource,
|
|
baseContext,
|
|
sourceMappingURL
|
|
);
|
|
|
|
if (!sourceContent) {
|
|
return output;
|
|
}
|
|
|
|
/** @type {RawSourceMap} */
|
|
const map = JSON.parse(
|
|
sourceContent.toString("utf8").replace(/^\)\]\}'/, "")
|
|
);
|
|
|
|
const context = sourceURL ? path.dirname(sourceURL) : baseContext;
|
|
|
|
const resolvedSources = await Promise.all(
|
|
map.sources.map(
|
|
async (/** @type {string} */ source, /** @type {number} */ i) => {
|
|
const originalSourceContent =
|
|
map.sourcesContent &&
|
|
typeof map.sourcesContent[i] !== "undefined" &&
|
|
map.sourcesContent[i] !== null
|
|
? map.sourcesContent[i]
|
|
: undefined;
|
|
const skipReading = typeof originalSourceContent !== "undefined";
|
|
// We do not skipReading here, because we need absolute paths in sources.
|
|
// This is necessary so that for sourceMaps with the same file structure in sources, name collisions do not occur.
|
|
// https://github.com/webpack-contrib/source-map-loader/issues/51
|
|
let { sourceURL, sourceContent } = await fetchFromURL(
|
|
readResource,
|
|
context,
|
|
source,
|
|
map.sourceRoot,
|
|
skipReading
|
|
);
|
|
|
|
if (skipReading) {
|
|
sourceContent = originalSourceContent;
|
|
}
|
|
|
|
// Return original value of `source` when error happens
|
|
return { sourceURL, sourceContent };
|
|
}
|
|
)
|
|
);
|
|
|
|
/** @type {RawSourceMap} */
|
|
const newMap = { ...map };
|
|
|
|
newMap.sources = [];
|
|
newMap.sourcesContent = [];
|
|
|
|
delete newMap.sourceRoot;
|
|
|
|
for (const source of resolvedSources) {
|
|
const { sourceURL, sourceContent } = source;
|
|
|
|
newMap.sources.push(sourceURL || "");
|
|
newMap.sourcesContent.push(
|
|
sourceContent ? sourceContent.toString("utf8") : ""
|
|
);
|
|
}
|
|
|
|
const sourcesContentIsEmpty =
|
|
newMap.sourcesContent.filter(Boolean).length === 0;
|
|
|
|
if (sourcesContentIsEmpty) {
|
|
delete newMap.sourcesContent;
|
|
}
|
|
|
|
return {
|
|
source: input.replace(replacementString, ""),
|
|
sourceMap: /** @type {RawSourceMap} */ (newMap)
|
|
};
|
|
}
|
|
|
|
module.exports = extractSourceMap;
|
|
module.exports.getSourceMappingURL = getSourceMappingURL;
|