🐛 Update: Added support for the 'find' command in settings.local.json. Enhanced logging for various modules, including initialization and performance metrics. Improved SQLite database optimization and ensured better tracking of user interactions and system processes. 📚

This commit is contained in:
2025-06-14 16:26:43 +02:00
parent ee54bc273c
commit 89037861e3
2472 changed files with 691099 additions and 1 deletions

View File

@@ -0,0 +1,177 @@
'use strict';
var use = require('use');
var define = require('define-property');
var debug = require('debug')('snapdragon:compiler');
var utils = require('./utils');
/**
* Create a new `Compiler` with the given `options`.
* @param {Object} `options`
*/
function Compiler(options, state) {
debug('initializing', __filename);
this.options = utils.extend({source: 'string'}, options);
this.state = state || {};
this.compilers = {};
this.output = '';
this.set('eos', function(node) {
return this.emit(node.val, node);
});
this.set('noop', function(node) {
return this.emit(node.val, node);
});
this.set('bos', function(node) {
return this.emit(node.val, node);
});
use(this);
}
/**
* Prototype methods
*/
Compiler.prototype = {
/**
* Throw an error message with details including the cursor position.
* @param {String} `msg` Message to use in the Error.
*/
error: function(msg, node) {
var pos = node.position || {start: {column: 0}};
var message = this.options.source + ' column:' + pos.start.column + ': ' + msg;
var err = new Error(message);
err.reason = msg;
err.column = pos.start.column;
err.source = this.pattern;
if (this.options.silent) {
this.errors.push(err);
} else {
throw err;
}
},
/**
* Define a non-enumberable property on the `Compiler` instance.
*
* ```js
* compiler.define('foo', 'bar');
* ```
* @name .define
* @param {String} `key` propery name
* @param {any} `val` property value
* @return {Object} Returns the Compiler instance for chaining.
* @api public
*/
define: function(key, val) {
define(this, key, val);
return this;
},
/**
* Emit `node.val`
*/
emit: function(str, node) {
this.output += str;
return str;
},
/**
* Add a compiler `fn` with the given `name`
*/
set: function(name, fn) {
this.compilers[name] = fn;
return this;
},
/**
* Get compiler `name`.
*/
get: function(name) {
return this.compilers[name];
},
/**
* Get the previous AST node.
*/
prev: function(n) {
return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' };
},
/**
* Get the next AST node.
*/
next: function(n) {
return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' };
},
/**
* Visit `node`.
*/
visit: function(node, nodes, i) {
var fn = this.compilers[node.type];
this.idx = i;
if (typeof fn !== 'function') {
throw this.error('compiler "' + node.type + '" is not registered', node);
}
return fn.call(this, node, nodes, i);
},
/**
* Map visit over array of `nodes`.
*/
mapVisit: function(nodes) {
if (!Array.isArray(nodes)) {
throw new TypeError('expected an array');
}
var len = nodes.length;
var idx = -1;
while (++idx < len) {
this.visit(nodes[idx], nodes, idx);
}
return this;
},
/**
* Compile `ast`.
*/
compile: function(ast, options) {
var opts = utils.extend({}, this.options, options);
this.ast = ast;
this.parsingErrors = this.ast.errors;
this.output = '';
// source map support
if (opts.sourcemap) {
var sourcemaps = require('./source-maps');
sourcemaps(this);
this.mapVisit(this.ast.nodes);
this.applySourceMaps();
this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON();
return this;
}
this.mapVisit(this.ast.nodes);
return this;
}
};
/**
* Expose `Compiler`
*/
module.exports = Compiler;

View File

@@ -0,0 +1,533 @@
'use strict';
var use = require('use');
var util = require('util');
var Cache = require('map-cache');
var define = require('define-property');
var debug = require('debug')('snapdragon:parser');
var Position = require('./position');
var utils = require('./utils');
/**
* Create a new `Parser` with the given `input` and `options`.
* @param {String} `input`
* @param {Object} `options`
* @api public
*/
function Parser(options) {
debug('initializing', __filename);
this.options = utils.extend({source: 'string'}, options);
this.init(this.options);
use(this);
}
/**
* Prototype methods
*/
Parser.prototype = {
constructor: Parser,
init: function(options) {
this.orig = '';
this.input = '';
this.parsed = '';
this.column = 1;
this.line = 1;
this.regex = new Cache();
this.errors = this.errors || [];
this.parsers = this.parsers || {};
this.types = this.types || [];
this.sets = this.sets || {};
this.fns = this.fns || [];
this.currentType = 'root';
var pos = this.position();
this.bos = pos({type: 'bos', val: ''});
this.ast = {
type: 'root',
errors: this.errors,
nodes: [this.bos]
};
define(this.bos, 'parent', this.ast);
this.nodes = [this.ast];
this.count = 0;
this.setCount = 0;
this.stack = [];
},
/**
* Throw a formatted error with the cursor column and `msg`.
* @param {String} `msg` Message to use in the Error.
*/
error: function(msg, node) {
var pos = node.position || {start: {column: 0, line: 0}};
var line = pos.start.line;
var column = pos.start.column;
var source = this.options.source;
var message = source + ' <line:' + line + ' column:' + column + '>: ' + msg;
var err = new Error(message);
err.source = source;
err.reason = msg;
err.pos = pos;
if (this.options.silent) {
this.errors.push(err);
} else {
throw err;
}
},
/**
* Define a non-enumberable property on the `Parser` instance.
*
* ```js
* parser.define('foo', 'bar');
* ```
* @name .define
* @param {String} `key` propery name
* @param {any} `val` property value
* @return {Object} Returns the Parser instance for chaining.
* @api public
*/
define: function(key, val) {
define(this, key, val);
return this;
},
/**
* Mark position and patch `node.position`.
*/
position: function() {
var start = { line: this.line, column: this.column };
var self = this;
return function(node) {
define(node, 'position', new Position(start, self));
return node;
};
},
/**
* Set parser `name` with the given `fn`
* @param {String} `name`
* @param {Function} `fn`
* @api public
*/
set: function(type, fn) {
if (this.types.indexOf(type) === -1) {
this.types.push(type);
}
this.parsers[type] = fn.bind(this);
return this;
},
/**
* Get parser `name`
* @param {String} `name`
* @api public
*/
get: function(name) {
return this.parsers[name];
},
/**
* Push a `token` onto the `type` stack.
*
* @param {String} `type`
* @return {Object} `token`
* @api public
*/
push: function(type, token) {
this.sets[type] = this.sets[type] || [];
this.count++;
this.stack.push(token);
return this.sets[type].push(token);
},
/**
* Pop a token off of the `type` stack
* @param {String} `type`
* @returns {Object} Returns a token
* @api public
*/
pop: function(type) {
this.sets[type] = this.sets[type] || [];
this.count--;
this.stack.pop();
return this.sets[type].pop();
},
/**
* Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`.
*
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isInside: function(type) {
this.sets[type] = this.sets[type] || [];
return this.sets[type].length > 0;
},
/**
* Return true if `node` is the given `type`.
*
* ```js
* parser.isType(node, 'brace');
* ```
* @param {Object} `node`
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isType: function(node, type) {
return node && node.type === type;
},
/**
* Get the previous AST node
* @return {Object}
*/
prev: function(n) {
return this.stack.length > 0
? utils.last(this.stack, n)
: utils.last(this.nodes, n);
},
/**
* Update line and column based on `str`.
*/
consume: function(len) {
this.input = this.input.substr(len);
},
/**
* Update column based on `str`.
*/
updatePosition: function(str, len) {
var lines = str.match(/\n/g);
if (lines) this.line += lines.length;
var i = str.lastIndexOf('\n');
this.column = ~i ? len - i : this.column + len;
this.parsed += str;
this.consume(len);
},
/**
* Match `regex`, return captures, and update the cursor position by `match[0]` length.
* @param {RegExp} `regex`
* @return {Object}
*/
match: function(regex) {
var m = regex.exec(this.input);
if (m) {
this.updatePosition(m[0], m[0].length);
return m;
}
},
/**
* Capture `type` with the given regex.
* @param {String} `type`
* @param {RegExp} `regex`
* @return {Function}
*/
capture: function(type, regex) {
if (typeof regex === 'function') {
return this.set.apply(this, arguments);
}
this.regex.set(type, regex);
this.set(type, function() {
var parsed = this.parsed;
var pos = this.position();
var m = this.match(regex);
if (!m || !m[0]) return;
var prev = this.prev();
var node = pos({
type: type,
val: m[0],
parsed: parsed,
rest: this.input
});
if (m[1]) {
node.inner = m[1];
}
define(node, 'inside', this.stack.length > 0);
define(node, 'parent', prev);
prev.nodes.push(node);
}.bind(this));
return this;
},
/**
* Create a parser with open and close for parens,
* brackets or braces
*/
capturePair: function(type, openRegex, closeRegex, fn) {
this.sets[type] = this.sets[type] || [];
/**
* Open
*/
this.set(type + '.open', function() {
var parsed = this.parsed;
var pos = this.position();
var m = this.match(openRegex);
if (!m || !m[0]) return;
var val = m[0];
this.setCount++;
this.specialChars = true;
var open = pos({
type: type + '.open',
val: val,
rest: this.input
});
if (typeof m[1] !== 'undefined') {
open.inner = m[1];
}
var prev = this.prev();
var node = pos({
type: type,
nodes: [open]
});
define(node, 'rest', this.input);
define(node, 'parsed', parsed);
define(node, 'prefix', m[1]);
define(node, 'parent', prev);
define(open, 'parent', node);
if (typeof fn === 'function') {
fn.call(this, open, node);
}
this.push(type, node);
prev.nodes.push(node);
});
/**
* Close
*/
this.set(type + '.close', function() {
var pos = this.position();
var m = this.match(closeRegex);
if (!m || !m[0]) return;
var parent = this.pop(type);
var node = pos({
type: type + '.close',
rest: this.input,
suffix: m[1],
val: m[0]
});
if (!this.isType(parent, type)) {
if (this.options.strict) {
throw new Error('missing opening "' + type + '"');
}
this.setCount--;
node.escaped = true;
return node;
}
if (node.suffix === '\\') {
parent.escaped = true;
node.escaped = true;
}
parent.nodes.push(node);
define(node, 'parent', parent);
});
return this;
},
/**
* Capture end-of-string
*/
eos: function() {
var pos = this.position();
if (this.input) return;
var prev = this.prev();
while (prev.type !== 'root' && !prev.visited) {
if (this.options.strict === true) {
throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2));
}
if (!hasDelims(prev)) {
prev.parent.escaped = true;
prev.escaped = true;
}
visit(prev, function(node) {
if (!hasDelims(node.parent)) {
node.parent.escaped = true;
node.escaped = true;
}
});
prev = prev.parent;
}
var tok = pos({
type: 'eos',
val: this.append || ''
});
define(tok, 'parent', this.ast);
return tok;
},
/**
* Run parsers to advance the cursor position
*/
next: function() {
var parsed = this.parsed;
var len = this.types.length;
var idx = -1;
var tok;
while (++idx < len) {
if ((tok = this.parsers[this.types[idx]].call(this))) {
define(tok, 'rest', this.input);
define(tok, 'parsed', parsed);
this.last = tok;
return tok;
}
}
},
/**
* Parse the given string.
* @return {Array}
*/
parse: function(input) {
if (typeof input !== 'string') {
throw new TypeError('expected a string');
}
this.init(this.options);
this.orig = input;
this.input = input;
var self = this;
function parse() {
// check input before calling `.next()`
input = self.input;
// get the next AST ndoe
var node = self.next();
if (node) {
var prev = self.prev();
if (prev) {
define(node, 'parent', prev);
if (prev.nodes) {
prev.nodes.push(node);
}
}
if (self.sets.hasOwnProperty(prev.type)) {
self.currentType = prev.type;
}
}
// if we got here but input is not changed, throw an error
if (self.input && input === self.input) {
throw new Error('no parsers registered for: "' + self.input.slice(0, 5) + '"');
}
}
while (this.input) parse();
if (this.stack.length && this.options.strict) {
var node = this.stack.pop();
throw this.error('missing opening ' + node.type + ': "' + this.orig + '"');
}
var eos = this.eos();
var tok = this.prev();
if (tok.type !== 'eos') {
this.ast.nodes.push(eos);
}
return this.ast;
}
};
/**
* Visit `node` with the given `fn`
*/
function visit(node, fn) {
if (!node.visited) {
define(node, 'visited', true);
return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
}
return node;
}
/**
* Map visit over array of `nodes`.
*/
function mapVisit(nodes, fn) {
var len = nodes.length;
var idx = -1;
while (++idx < len) {
visit(nodes[idx], fn);
}
}
function hasOpen(node) {
return node.nodes && node.nodes[0].type === (node.type + '.open');
}
function hasClose(node) {
return node.nodes && utils.last(node.nodes).type === (node.type + '.close');
}
function hasDelims(node) {
return hasOpen(node) && hasClose(node);
}
/**
* Expose `Parser`
*/
module.exports = Parser;

View File

@@ -0,0 +1,14 @@
'use strict';
var define = require('define-property');
/**
* Store position for a node
*/
module.exports = function Position(start, parser) {
this.start = start;
this.end = { line: parser.line, column: parser.column };
define(this, 'content', parser.orig);
define(this, 'source', parser.options.source);
};

View File

@@ -0,0 +1,145 @@
'use strict';
var fs = require('fs');
var path = require('path');
var define = require('define-property');
var utils = require('./utils');
/**
* Expose `mixin()`.
* This code is based on `source-maps-support.js` in reworkcss/css
* https://github.com/reworkcss/css/blob/master/lib/stringify/source-map-support.js
* Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
*/
module.exports = mixin;
/**
* Mixin source map support into `compiler`.
*
* @param {Object} `compiler`
* @api public
*/
function mixin(compiler) {
define(compiler, '_comment', compiler.comment);
compiler.map = new utils.SourceMap.SourceMapGenerator();
compiler.position = { line: 1, column: 1 };
compiler.content = {};
compiler.files = {};
for (var key in exports) {
define(compiler, key, exports[key]);
}
}
/**
* Update position.
*
* @param {String} str
*/
exports.updatePosition = function(str) {
var lines = str.match(/\n/g);
if (lines) this.position.line += lines.length;
var i = str.lastIndexOf('\n');
this.position.column = ~i ? str.length - i : this.position.column + str.length;
};
/**
* Emit `str` with `position`.
*
* @param {String} str
* @param {Object} [pos]
* @return {String}
*/
exports.emit = function(str, node) {
var position = node.position || {};
var source = position.source;
if (source) {
if (position.filepath) {
source = utils.unixify(position.filepath);
}
this.map.addMapping({
source: source,
generated: {
line: this.position.line,
column: Math.max(this.position.column - 1, 0)
},
original: {
line: position.start.line,
column: position.start.column - 1
}
});
if (position.content) {
this.addContent(source, position);
}
if (position.filepath) {
this.addFile(source, position);
}
this.updatePosition(str);
this.output += str;
}
return str;
};
/**
* Adds a file to the source map output if it has not already been added
* @param {String} `file`
* @param {Object} `pos`
*/
exports.addFile = function(file, position) {
if (typeof position.content !== 'string') return;
if (Object.prototype.hasOwnProperty.call(this.files, file)) return;
this.files[file] = position.content;
};
/**
* Adds a content source to the source map output if it has not already been added
* @param {String} `source`
* @param {Object} `position`
*/
exports.addContent = function(source, position) {
if (typeof position.content !== 'string') return;
if (Object.prototype.hasOwnProperty.call(this.content, source)) return;
this.map.setSourceContent(source, position.content);
};
/**
* Applies any original source maps to the output and embeds the source file
* contents in the source map.
*/
exports.applySourceMaps = function() {
Object.keys(this.files).forEach(function(file) {
var content = this.files[file];
this.map.setSourceContent(file, content);
if (this.options.inputSourcemaps === true) {
var originalMap = utils.sourceMapResolve.resolveSync(content, file, fs.readFileSync);
if (originalMap) {
var map = new utils.SourceMap.SourceMapConsumer(originalMap.map);
var relativeTo = originalMap.sourcesRelativeTo;
this.map.applySourceMap(map, file, utils.unixify(path.dirname(relativeTo)));
}
}
}, this);
};
/**
* Process comments, drops sourceMap comments.
* @param {Object} node
*/
exports.comment = function(node) {
if (/^# sourceMappingURL=/.test(node.comment)) {
return this.emit('', node.position);
}
return this._comment(node);
};

View File

@@ -0,0 +1,48 @@
'use strict';
/**
* Module dependencies
*/
exports.extend = require('extend-shallow');
exports.SourceMap = require('source-map');
exports.sourceMapResolve = require('source-map-resolve');
/**
* Convert backslash in the given string to forward slashes
*/
exports.unixify = function(fp) {
return fp.split(/\\+/).join('/');
};
/**
* Return true if `val` is a non-empty string
*
* @param {String} `str`
* @return {Boolean}
*/
exports.isString = function(str) {
return str && typeof str === 'string';
};
/**
* Cast `val` to an array
* @return {Array}
*/
exports.arrayify = function(val) {
if (typeof val === 'string') return [val];
return val ? (Array.isArray(val) ? val : [val]) : [];
};
/**
* Get the last `n` element from the given `array`
* @param {Array} `array`
* @return {*}
*/
exports.last = function(arr, n) {
return arr[arr.length - (n || 1)];
};