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>
232 lines
6.6 KiB
JavaScript
Executable File
232 lines
6.6 KiB
JavaScript
Executable File
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const Template = require("../Template");
|
|
|
|
/** @typedef {import("eslint-scope").Scope} Scope */
|
|
/** @typedef {import("eslint-scope").Reference} Reference */
|
|
/** @typedef {import("eslint-scope").Variable} Variable */
|
|
/** @typedef {import("estree").Node} Node */
|
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
/** @typedef {Set<string>} UsedNames */
|
|
|
|
const DEFAULT_EXPORT = "__WEBPACK_DEFAULT_EXPORT__";
|
|
const NAMESPACE_OBJECT_EXPORT = "__WEBPACK_NAMESPACE_OBJECT__";
|
|
|
|
/**
|
|
* @param {Variable} variable variable
|
|
* @returns {Reference[]} references
|
|
*/
|
|
const getAllReferences = (variable) => {
|
|
let set = variable.references;
|
|
// Look for inner scope variables too (like in class Foo { t() { Foo } })
|
|
const identifiers = new Set(variable.identifiers);
|
|
for (const scope of variable.scope.childScopes) {
|
|
for (const innerVar of scope.variables) {
|
|
if (innerVar.identifiers.some((id) => identifiers.has(id))) {
|
|
set = [...set, ...innerVar.references];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return set;
|
|
};
|
|
|
|
/**
|
|
* @param {Node | Node[]} ast ast
|
|
* @param {Node} node node
|
|
* @returns {undefined | Node[]} result
|
|
*/
|
|
const getPathInAst = (ast, node) => {
|
|
if (ast === node) {
|
|
return [];
|
|
}
|
|
|
|
const nr = /** @type {Range} */ (node.range);
|
|
|
|
/**
|
|
* @param {Node} n node
|
|
* @returns {Node[] | undefined} result
|
|
*/
|
|
const enterNode = (n) => {
|
|
if (!n) return;
|
|
const r = n.range;
|
|
if (r && r[0] <= nr[0] && r[1] >= nr[1]) {
|
|
const path = getPathInAst(n, node);
|
|
if (path) {
|
|
path.push(n);
|
|
return path;
|
|
}
|
|
}
|
|
};
|
|
|
|
if (Array.isArray(ast)) {
|
|
for (let i = 0; i < ast.length; i++) {
|
|
const enterResult = enterNode(ast[i]);
|
|
if (enterResult !== undefined) return enterResult;
|
|
}
|
|
} else if (ast && typeof ast === "object") {
|
|
const keys =
|
|
/** @type {(keyof Node)[]} */
|
|
(Object.keys(ast));
|
|
for (let i = 0; i < keys.length; i++) {
|
|
// We are making the faster check in `enterNode` using `n.range`
|
|
const value =
|
|
ast[
|
|
/** @type {Exclude<keyof Node, "range" | "loc" | "leadingComments" | "trailingComments">} */
|
|
(keys[i])
|
|
];
|
|
if (Array.isArray(value)) {
|
|
const pathResult = getPathInAst(value, node);
|
|
if (pathResult !== undefined) return pathResult;
|
|
} else if (value && typeof value === "object") {
|
|
const enterResult = enterNode(value);
|
|
if (enterResult !== undefined) return enterResult;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string} oldName old name
|
|
* @param {UsedNames} usedNamed1 used named 1
|
|
* @param {UsedNames} usedNamed2 used named 2
|
|
* @param {string} extraInfo extra info
|
|
* @returns {string} found new name
|
|
*/
|
|
function findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
|
|
let name = oldName;
|
|
|
|
if (name === DEFAULT_EXPORT) {
|
|
name = "";
|
|
}
|
|
if (name === NAMESPACE_OBJECT_EXPORT) {
|
|
name = "namespaceObject";
|
|
}
|
|
|
|
// Remove uncool stuff
|
|
extraInfo = extraInfo.replace(
|
|
/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
|
|
""
|
|
);
|
|
|
|
const splittedInfo = extraInfo.split("/");
|
|
while (splittedInfo.length) {
|
|
name = splittedInfo.pop() + (name ? `_${name}` : "");
|
|
const nameIdent = Template.toIdentifier(name);
|
|
if (
|
|
!usedNamed1.has(nameIdent) &&
|
|
(!usedNamed2 || !usedNamed2.has(nameIdent))
|
|
) {
|
|
return nameIdent;
|
|
}
|
|
}
|
|
|
|
let i = 0;
|
|
let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
while (
|
|
usedNamed1.has(nameWithNumber) ||
|
|
// eslint-disable-next-line no-unmodified-loop-condition
|
|
(usedNamed2 && usedNamed2.has(nameWithNumber))
|
|
) {
|
|
i++;
|
|
nameWithNumber = Template.toIdentifier(`${name}_${i}`);
|
|
}
|
|
return nameWithNumber;
|
|
}
|
|
|
|
/** @typedef {Set<Scope>} ScopeSet */
|
|
|
|
/**
|
|
* @param {Scope | null} s scope
|
|
* @param {UsedNames} nameSet name set
|
|
* @param {ScopeSet} scopeSet1 scope set 1
|
|
* @param {ScopeSet} scopeSet2 scope set 2
|
|
*/
|
|
const addScopeSymbols = (s, nameSet, scopeSet1, scopeSet2) => {
|
|
let scope = s;
|
|
while (scope) {
|
|
if (scopeSet1.has(scope)) break;
|
|
if (scopeSet2.has(scope)) break;
|
|
scopeSet1.add(scope);
|
|
for (const variable of scope.variables) {
|
|
nameSet.add(variable.name);
|
|
}
|
|
scope = scope.upper;
|
|
}
|
|
};
|
|
|
|
const RESERVED_NAMES = new Set(
|
|
[
|
|
// internal names (should always be renamed)
|
|
DEFAULT_EXPORT,
|
|
NAMESPACE_OBJECT_EXPORT,
|
|
|
|
// keywords
|
|
"abstract,arguments,async,await,boolean,break,byte,case,catch,char,class,const,continue",
|
|
"debugger,default,delete,do,double,else,enum,eval,export,extends,false,final,finally,float",
|
|
"for,function,goto,if,implements,import,in,instanceof,int,interface,let,long,native,new,null",
|
|
"package,private,protected,public,return,short,static,super,switch,synchronized,this,throw",
|
|
"throws,transient,true,try,typeof,var,void,volatile,while,with,yield",
|
|
|
|
// commonjs/amd
|
|
"module,__dirname,__filename,exports,require,define",
|
|
|
|
// js globals
|
|
"Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
|
|
"NaN,name,Number,Object,prototype,String,Symbol,toString,undefined,valueOf",
|
|
|
|
// browser globals
|
|
"alert,all,anchor,anchors,area,assign,blur,button,checkbox,clearInterval,clearTimeout",
|
|
"clientInformation,close,closed,confirm,constructor,crypto,decodeURI,decodeURIComponent",
|
|
"defaultStatus,document,element,elements,embed,embeds,encodeURI,encodeURIComponent,escape",
|
|
"event,fileUpload,focus,form,forms,frame,innerHeight,innerWidth,layer,layers,link,location",
|
|
"mimeTypes,navigate,navigator,frames,frameRate,hidden,history,image,images,offscreenBuffering",
|
|
"open,opener,option,outerHeight,outerWidth,packages,pageXOffset,pageYOffset,parent,parseFloat",
|
|
"parseInt,password,pkcs11,plugin,prompt,propertyIsEnum,radio,reset,screenX,screenY,scroll",
|
|
"secure,select,self,setInterval,setTimeout,status,submit,taint,text,textarea,top,unescape",
|
|
"untaint,window",
|
|
|
|
// window events
|
|
"onblur,onclick,onerror,onfocus,onkeydown,onkeypress,onkeyup,onmouseover,onload,onmouseup,onmousedown,onsubmit"
|
|
]
|
|
.join(",")
|
|
.split(",")
|
|
);
|
|
|
|
/** @typedef {{ usedNames: UsedNames, alreadyCheckedScopes: ScopeSet }} ScopeInfo */
|
|
|
|
/**
|
|
* @param {Map<string, ScopeInfo>} usedNamesInScopeInfo used names in scope info
|
|
* @param {string} module module identifier
|
|
* @param {string} id export id
|
|
* @returns {ScopeInfo} info
|
|
*/
|
|
const getUsedNamesInScopeInfo = (usedNamesInScopeInfo, module, id) => {
|
|
const key = `${module}-${id}`;
|
|
let info = usedNamesInScopeInfo.get(key);
|
|
if (info === undefined) {
|
|
info = {
|
|
usedNames: new Set(),
|
|
alreadyCheckedScopes: new Set()
|
|
};
|
|
usedNamesInScopeInfo.set(key, info);
|
|
}
|
|
return info;
|
|
};
|
|
|
|
module.exports = {
|
|
DEFAULT_EXPORT,
|
|
NAMESPACE_OBJECT_EXPORT,
|
|
RESERVED_NAMES,
|
|
addScopeSymbols,
|
|
findNewName,
|
|
getAllReferences,
|
|
getPathInAst,
|
|
getUsedNamesInScopeInfo
|
|
};
|