WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { _ as _inherits } from "./_inherits.js";
|
||||
import { _ as _set_prototype_of } from "./_set_prototype_of.js";
|
||||
|
||||
function _wrap_reg_exp(re, groups, source) {
|
||||
_wrap_reg_exp = function(re, groups, source) {
|
||||
return new WrappedRegExp(re, undefined, groups, source);
|
||||
};
|
||||
var _super = RegExp.prototype;
|
||||
var _groups = new WeakMap();
|
||||
var _sources = new WeakMap();
|
||||
var _native_source = Object.getOwnPropertyDescriptor(_super, "source").get;
|
||||
function WrappedRegExp(re, flags, groups, source) {
|
||||
var _re = new RegExp(re, flags);
|
||||
_groups.set(_re, groups || _groups.get(re));
|
||||
_sources.set(_re, source !== undefined ? source : _sources.get(re));
|
||||
return _set_prototype_of(_re, WrappedRegExp.prototype);
|
||||
}
|
||||
_inherits(WrappedRegExp, RegExp);
|
||||
Object.defineProperty(WrappedRegExp.prototype, "source", {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
var source = _sources.get(this);
|
||||
if (source !== undefined) {
|
||||
try {
|
||||
new RegExp(source, this.flags);
|
||||
return source;
|
||||
} catch (_) {}
|
||||
}
|
||||
return _native_source.call(this);
|
||||
}
|
||||
});
|
||||
WrappedRegExp.prototype.exec = function(str) {
|
||||
var result = _super.exec.call(this, str);
|
||||
if (result) {
|
||||
result.groups = buildGroups(result, this);
|
||||
var indices = result.indices;
|
||||
if (indices) indices.groups = buildGroups(indices, this);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
WrappedRegExp.prototype[Symbol.replace] = function(str, substitution) {
|
||||
if (typeof substitution === "string") {
|
||||
var groups = _groups.get(this);
|
||||
return _super[Symbol.replace].call(
|
||||
this,
|
||||
str,
|
||||
substitution.replace(/\$<([^>]+)>/g, function(_, name) {
|
||||
var group = groups ? groups[name] : undefined;
|
||||
if (group === undefined) return "";
|
||||
return "$" + (Array.isArray(group) ? group.join("$") : group);
|
||||
})
|
||||
);
|
||||
}
|
||||
if (typeof substitution === "function") {
|
||||
var _this = this;
|
||||
return _super[Symbol.replace].call(this, str, function() {
|
||||
var args = arguments;
|
||||
if (typeof args[args.length - 1] !== "object") {
|
||||
args = [].slice.call(args);
|
||||
args.push(buildGroups(args, _this));
|
||||
}
|
||||
return substitution.apply(this, args);
|
||||
});
|
||||
}
|
||||
return _super[Symbol.replace].call(this, str, substitution);
|
||||
};
|
||||
function buildGroups(result, re) {
|
||||
var g = _groups.get(re);
|
||||
return Object.keys(g).reduce(function(groups, name) {
|
||||
var i = g[name];
|
||||
if (typeof i === "number") groups[name] = result[i];
|
||||
else {
|
||||
var k = 0;
|
||||
while (result[i[k]] === undefined && k + 1 < i.length) k++;
|
||||
groups[name] = result[i[k]];
|
||||
}
|
||||
return groups;
|
||||
}, Object.create(null));
|
||||
}
|
||||
return _wrap_reg_exp.apply(this, arguments);
|
||||
}
|
||||
export { _wrap_reg_exp as _ };
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
interface Colors {
|
||||
comment: {
|
||||
close: string;
|
||||
open: string;
|
||||
};
|
||||
content: {
|
||||
close: string;
|
||||
open: string;
|
||||
};
|
||||
prop: {
|
||||
close: string;
|
||||
open: string;
|
||||
};
|
||||
tag: {
|
||||
close: string;
|
||||
open: string;
|
||||
};
|
||||
value: {
|
||||
close: string;
|
||||
open: string;
|
||||
};
|
||||
}
|
||||
type Indent = (arg0: string) => string;
|
||||
type Refs = Array<unknown>;
|
||||
type Print = (arg0: unknown) => string;
|
||||
type Theme = Required<{
|
||||
comment?: string;
|
||||
content?: string;
|
||||
prop?: string;
|
||||
tag?: string;
|
||||
value?: string;
|
||||
}>;
|
||||
/**
|
||||
* compare function used when sorting object keys, `null` can be used to skip over sorting.
|
||||
*/
|
||||
type CompareKeys = ((a: string, b: string) => number) | null | undefined;
|
||||
type RequiredOptions = Required<PrettyFormatOptions>;
|
||||
interface Options extends Omit<RequiredOptions, "compareKeys" | "theme"> {
|
||||
compareKeys: CompareKeys;
|
||||
theme: Theme;
|
||||
}
|
||||
interface PrettyFormatOptions {
|
||||
/**
|
||||
* Call `toJSON` on objects before formatting them.
|
||||
* Ignored after the formatter has already called `toJSON` once for a value.
|
||||
* @default true
|
||||
*/
|
||||
callToJSON?: boolean;
|
||||
/**
|
||||
* Whether to escape special characters in regular expressions.
|
||||
* @default false
|
||||
*/
|
||||
escapeRegex?: boolean;
|
||||
/**
|
||||
* Whether to escape special characters in strings.
|
||||
* @default true
|
||||
*/
|
||||
escapeString?: boolean;
|
||||
/**
|
||||
* Whether to highlight syntax using terminal colors.
|
||||
* @default false
|
||||
*/
|
||||
highlight?: boolean;
|
||||
/**
|
||||
* Number of spaces to use for each level of indentation.
|
||||
* @default 2
|
||||
*/
|
||||
indent?: number;
|
||||
/**
|
||||
* Maximum depth to recurse into nested values.
|
||||
* @default Infinity
|
||||
*/
|
||||
maxDepth?: number;
|
||||
/**
|
||||
* Maximum number of items to print in arrays, sets, maps, and similar collections.
|
||||
* @default Infinity
|
||||
*/
|
||||
maxWidth?: number;
|
||||
/**
|
||||
* Approximate per-depth-level budget for output length.
|
||||
* When the accumulated output at any single depth level exceeds this value,
|
||||
* further nesting is collapsed. This is a heuristic safety valve, not a hard
|
||||
* limit — total output can reach up to roughly `maxDepth × maxOutputLength`.
|
||||
* @default 1_000_000
|
||||
*/
|
||||
maxOutputLength?: number;
|
||||
/**
|
||||
* Whether to minimize added whitespace, including indentation and line breaks.
|
||||
* @default false
|
||||
*/
|
||||
min?: boolean;
|
||||
/**
|
||||
* Whether to print `Object` / `Array` prefixes for plain objects and arrays.
|
||||
* @default true
|
||||
*/
|
||||
printBasicPrototype?: boolean;
|
||||
/**
|
||||
* Whether to include the function name when formatting functions.
|
||||
* @default true
|
||||
*/
|
||||
printFunctionName?: boolean;
|
||||
/**
|
||||
* Whether to include shadow-root contents when formatting DOM nodes.
|
||||
* @default true
|
||||
*/
|
||||
printShadowRoot?: boolean;
|
||||
/**
|
||||
* Compare function used when sorting object keys. Set to `null` to disable sorting.
|
||||
*/
|
||||
compareKeys?: CompareKeys;
|
||||
/**
|
||||
* Plugins used to serialize application-specific data types.
|
||||
* @default []
|
||||
*/
|
||||
plugins?: Plugins;
|
||||
}
|
||||
type OptionsReceived = PrettyFormatOptions;
|
||||
interface Config {
|
||||
callToJSON: boolean;
|
||||
compareKeys: CompareKeys;
|
||||
colors: Colors;
|
||||
escapeRegex: boolean;
|
||||
escapeString: boolean;
|
||||
indent: string;
|
||||
maxDepth: number;
|
||||
maxWidth: number;
|
||||
min: boolean;
|
||||
plugins: Plugins;
|
||||
printBasicPrototype: boolean;
|
||||
printFunctionName: boolean;
|
||||
printShadowRoot: boolean;
|
||||
spacingInner: string;
|
||||
spacingOuter: string;
|
||||
maxOutputLength: number;
|
||||
}
|
||||
type Printer = (val: unknown, config: Config, indentation: string, depth: number, refs: Refs, hasCalledToJSON?: boolean) => string;
|
||||
type Test = (arg0: any) => boolean;
|
||||
interface NewPlugin {
|
||||
serialize: (val: any, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer) => string;
|
||||
test: Test;
|
||||
}
|
||||
interface PluginOptions {
|
||||
edgeSpacing: string;
|
||||
min: boolean;
|
||||
spacing: string;
|
||||
}
|
||||
interface OldPlugin {
|
||||
print: (val: unknown, print: Print, indent: Indent, options: PluginOptions, colors: Colors) => string;
|
||||
test: Test;
|
||||
}
|
||||
type Plugin = NewPlugin | OldPlugin;
|
||||
type Plugins = Array<Plugin>;
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare function createDOMElementFilter(filterNode?: (node: any) => boolean): NewPlugin;
|
||||
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
declare const DEFAULT_OPTIONS: Options;
|
||||
/**
|
||||
* Returns a presentation string of your `val` object
|
||||
* @param val any potential JavaScript object
|
||||
* @param options Custom settings
|
||||
*/
|
||||
declare function format(val: unknown, options?: OptionsReceived): string;
|
||||
|
||||
declare const plugins: {
|
||||
AsymmetricMatcher: NewPlugin;
|
||||
DOMCollection: NewPlugin;
|
||||
DOMElement: NewPlugin;
|
||||
Immutable: NewPlugin;
|
||||
ReactElement: NewPlugin;
|
||||
ReactTestComponent: NewPlugin;
|
||||
Error: NewPlugin;
|
||||
};
|
||||
|
||||
export { DEFAULT_OPTIONS, createDOMElementFilter, format, plugins };
|
||||
export type { Colors, CompareKeys, Config, NewPlugin, OldPlugin, Options, OptionsReceived, Plugin, Plugins, PrettyFormatOptions, Printer, Refs, Theme };
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Normalises alias mappings, ensuring that more specific aliases are resolved before less specific ones.
|
||||
* This function also ensures that aliases do not resolve to themselves cyclically.
|
||||
*
|
||||
* @param _aliases - A set of alias mappings where each key is an alias and its value is the actual path it points to.
|
||||
* @returns a set of normalised alias mappings.
|
||||
*/
|
||||
declare function normalizeAliases(_aliases: Record<string, string>): Record<string, string>;
|
||||
/**
|
||||
* Resolves a path string to its alias if applicable, otherwise returns the original path.
|
||||
* This function normalises the path, resolves the alias and then joins it to the alias target if necessary.
|
||||
*
|
||||
* @param path - The path string to resolve.
|
||||
* @param aliases - A set of alias mappings to use for resolution.
|
||||
* @returns the resolved path as a string.
|
||||
*/
|
||||
declare function resolveAlias(path: string, aliases: Record<string, string>): string;
|
||||
/**
|
||||
* Resolves a path string to its possible alias.
|
||||
*
|
||||
* Returns an array of possible alias resolutions (could be empty), sorted by specificity (longest first).
|
||||
*/
|
||||
declare function reverseResolveAlias(path: string, aliases: Record<string, string>): string[];
|
||||
/**
|
||||
* Extracts the filename from a given path, excluding any directory paths and the file extension.
|
||||
*
|
||||
* @param path - The full path of the file from which to extract the filename.
|
||||
* @returns the filename without the extension, or `undefined` if the filename cannot be extracted.
|
||||
*/
|
||||
declare function filename(path: string): string | undefined;
|
||||
|
||||
export { filename, normalizeAliases, resolveAlias, reverseResolveAlias };
|
||||
@@ -0,0 +1,23 @@
|
||||
export {};
|
||||
|
||||
import * as buffer from "node:buffer";
|
||||
|
||||
type _Blob = typeof globalThis extends { onmessage: any } ? {} : buffer.Blob;
|
||||
type _BlobPropertyBag = typeof globalThis extends { onmessage: any } ? {} : buffer.BlobPropertyBag;
|
||||
type _File = typeof globalThis extends { onmessage: any } ? {} : buffer.File;
|
||||
type _FilePropertyBag = typeof globalThis extends { onmessage: any } ? {} : buffer.FilePropertyBag;
|
||||
|
||||
declare global {
|
||||
interface Blob extends _Blob {}
|
||||
var Blob: typeof globalThis extends { onmessage: any; Blob: infer T } ? T : typeof buffer.Blob;
|
||||
|
||||
interface BlobPropertyBag extends _BlobPropertyBag {}
|
||||
|
||||
interface File extends _File {}
|
||||
var File: typeof globalThis extends { onmessage: any; File: infer T } ? T : typeof buffer.File;
|
||||
|
||||
interface FilePropertyBag extends _FilePropertyBag {}
|
||||
|
||||
function atob(data: string): string;
|
||||
function btoa(data: string): string;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
export * as core from "../core/index.js";
|
||||
export * from "./parse.js";
|
||||
export * from "./schemas.js";
|
||||
export * from "./checks.js";
|
||||
|
||||
export type { infer, output, input } from "../core/index.js";
|
||||
export type { JSONType } from "../core/util.js";
|
||||
export {
|
||||
globalRegistry,
|
||||
registry,
|
||||
config,
|
||||
$output,
|
||||
$input,
|
||||
$brand,
|
||||
clone,
|
||||
regexes,
|
||||
treeifyError,
|
||||
prettifyError,
|
||||
formatError,
|
||||
flattenError,
|
||||
TimePrecision,
|
||||
util,
|
||||
NEVER,
|
||||
} from "../core/index.js";
|
||||
export { toJSONSchema } from "../core/json-schema-processors.js";
|
||||
|
||||
export * as locales from "../locales/index.js";
|
||||
/** A special constant with type `never` */
|
||||
// export const NEVER = {} as never;
|
||||
|
||||
// iso
|
||||
export * as iso from "./iso.js";
|
||||
export {
|
||||
ZodMiniISODateTime,
|
||||
ZodMiniISODate,
|
||||
ZodMiniISOTime,
|
||||
ZodMiniISODuration,
|
||||
} from "./iso.js";
|
||||
|
||||
// coerce
|
||||
export * as coerce from "./coerce.js";
|
||||
@@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const config = require('./pkg.config.json')
|
||||
const { promisify } = require('util')
|
||||
const { unlink } = require('fs/promises')
|
||||
const { join } = require('path')
|
||||
const { platform } = require('process')
|
||||
const exec = promisify(require('child_process').exec)
|
||||
|
||||
test('worker test when packaged into executable using pkg', async () => {
|
||||
const packageName = 'index'
|
||||
|
||||
// package the app into several node versions, check config for more info
|
||||
const filePath = `${join(__dirname, packageName)}.js`
|
||||
const configPath = join(__dirname, 'pkg.config.json')
|
||||
process.env.NODE_OPTIONS ||= ''
|
||||
process.env.NODE_OPTIONS = '--no-warnings'
|
||||
const { stderr } = await exec(`npx pkg ${filePath} --config ${configPath}`)
|
||||
|
||||
// there should be no error when packaging
|
||||
assert.strictEqual(stderr, '')
|
||||
|
||||
// pkg outputs files in the following format by default: {filename}-{node version}
|
||||
for (const target of config.pkg.targets) {
|
||||
// execute the packaged test
|
||||
let executablePath = `${join(config.pkg.outputPath, packageName)}-${target}`
|
||||
|
||||
// when on windows, we need the .exe extension
|
||||
if (platform === 'win32') {
|
||||
executablePath = `${executablePath}.exe`
|
||||
} else {
|
||||
executablePath = `./${executablePath}`
|
||||
}
|
||||
|
||||
const { stderr } = await exec(executablePath)
|
||||
|
||||
// check if there were no errors
|
||||
assert.strictEqual(stderr, '')
|
||||
|
||||
// clean up afterwards
|
||||
await unlink(executablePath)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.clearProgramCache = void 0;
|
||||
exports.clearCaches = clearCaches;
|
||||
const getWatchProgramsForProjects_1 = require("./create-program/getWatchProgramsForProjects");
|
||||
const parser_1 = require("./parser");
|
||||
const candidateTSConfigRootDirs_1 = require("./parseSettings/candidateTSConfigRootDirs");
|
||||
const createParseSettings_1 = require("./parseSettings/createParseSettings");
|
||||
const resolveProjectList_1 = require("./parseSettings/resolveProjectList");
|
||||
/**
|
||||
* Clears all of the internal caches.
|
||||
* Generally you shouldn't need or want to use this.
|
||||
* Examples of intended uses:
|
||||
* - In tests to reset parser state to keep tests isolated.
|
||||
* - In custom lint tooling that iteratively lints one project at a time to prevent OOMs.
|
||||
*/
|
||||
function clearCaches() {
|
||||
(0, candidateTSConfigRootDirs_1.clearCandidateTSConfigRootDirs)();
|
||||
(0, parser_1.clearDefaultProjectMatchedFiles)();
|
||||
(0, parser_1.clearProgramCache)();
|
||||
(0, getWatchProgramsForProjects_1.clearWatchCaches)();
|
||||
(0, createParseSettings_1.clearTSConfigMatchCache)();
|
||||
(0, createParseSettings_1.clearTSServerProjectService)();
|
||||
(0, resolveProjectList_1.clearGlobCache)();
|
||||
}
|
||||
// TODO - delete this in next major
|
||||
exports.clearProgramCache = clearCaches;
|
||||
@@ -0,0 +1,21 @@
|
||||
var cp = require('child_process');
|
||||
|
||||
/**
|
||||
* Returns the version string of the current working directory.
|
||||
* It is the git short hash of the last commit that changed filePath
|
||||
* If there are uncommitted changes to this filePath, this method will throw.
|
||||
* @returns {string}
|
||||
*/
|
||||
module.exports = function getGitHashSync(filePath) {
|
||||
var commandResult;
|
||||
try {
|
||||
cp.execSync('git diff-index --quiet HEAD -- ' + filePath, { encoding: 'utf-8' });
|
||||
} catch (err) {
|
||||
//throw new Error('Cannot resolve git hash of file ' + filePath + ': There are uncommitted changes to file ' + filePath);
|
||||
}
|
||||
commandResult = cp.execSync('git log -n1 --pretty=format:%h -- ' + filePath, { encoding: 'utf-8' });
|
||||
if (!commandResult) {
|
||||
throw new Error("Could not get hash: file " + filePath + " does not exist");
|
||||
}
|
||||
return commandResult;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const { join } = require('path')
|
||||
const { once } = require('events')
|
||||
const { MessageChannel } = require('worker_threads')
|
||||
const ThreadStream = require('..')
|
||||
|
||||
test('message events emitted on the stream are posted to the worker', async function (t) {
|
||||
const { port1, port2 } = new MessageChannel()
|
||||
const stream = new ThreadStream({
|
||||
filename: join(__dirname, 'on-message.js'),
|
||||
sync: false
|
||||
})
|
||||
t.after(() => {
|
||||
stream.end()
|
||||
})
|
||||
|
||||
stream.emit('message', { text: 'hello', takeThisPortPlease: port1 }, [port1])
|
||||
const [confirmation] = await once(port2, 'message')
|
||||
assert.strictEqual(confirmation, 'received: hello')
|
||||
})
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { ParserServicesWithTypeInformation, TSESTree } from '@typescript-eslint/utils';
|
||||
import type { ReportDescriptor, RuleContext } from '@typescript-eslint/utils/ts-eslint';
|
||||
import type { PreferOptionalChainMessageIds, PreferOptionalChainOptions } from './PreferOptionalChainOptions';
|
||||
export declare function checkNullishAndReport(context: RuleContext<PreferOptionalChainMessageIds, [
|
||||
PreferOptionalChainOptions
|
||||
]>, parserServices: ParserServicesWithTypeInformation, { requireNullish }: PreferOptionalChainOptions, maybeNullishNodes: TSESTree.Expression[], descriptor: ReportDescriptor<PreferOptionalChainMessageIds>): void;
|
||||
@@ -0,0 +1,747 @@
|
||||
const { humanReadableArgName } = require('./argument.js');
|
||||
|
||||
/**
|
||||
* TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS`
|
||||
* https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
|
||||
* @typedef { import("./argument.js").Argument } Argument
|
||||
* @typedef { import("./command.js").Command } Command
|
||||
* @typedef { import("./option.js").Option } Option
|
||||
*/
|
||||
|
||||
// Although this is a class, methods are static in style to allow override using subclass or just functions.
|
||||
class Help {
|
||||
constructor() {
|
||||
this.helpWidth = undefined;
|
||||
this.minWidthToWrap = 40;
|
||||
this.sortSubcommands = false;
|
||||
this.sortOptions = false;
|
||||
this.showGlobalOptions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepareContext is called by Commander after applying overrides from `Command.configureHelp()`
|
||||
* and just before calling `formatHelp()`.
|
||||
*
|
||||
* Commander just uses the helpWidth and the rest is provided for optional use by more complex subclasses.
|
||||
*
|
||||
* @param {{ error?: boolean, helpWidth?: number, outputHasColors?: boolean }} contextOptions
|
||||
*/
|
||||
prepareContext(contextOptions) {
|
||||
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Command[]}
|
||||
*/
|
||||
|
||||
visibleCommands(cmd) {
|
||||
const visibleCommands = cmd.commands.filter((cmd) => !cmd._hidden);
|
||||
const helpCommand = cmd._getHelpCommand();
|
||||
if (helpCommand && !helpCommand._hidden) {
|
||||
visibleCommands.push(helpCommand);
|
||||
}
|
||||
if (this.sortSubcommands) {
|
||||
visibleCommands.sort((a, b) => {
|
||||
// @ts-ignore: because overloaded return type
|
||||
return a.name().localeCompare(b.name());
|
||||
});
|
||||
}
|
||||
return visibleCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare options for sort.
|
||||
*
|
||||
* @param {Option} a
|
||||
* @param {Option} b
|
||||
* @returns {number}
|
||||
*/
|
||||
compareOptions(a, b) {
|
||||
const getSortKey = (option) => {
|
||||
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
|
||||
return option.short
|
||||
? option.short.replace(/^-/, '')
|
||||
: option.long.replace(/^--/, '');
|
||||
};
|
||||
return getSortKey(a).localeCompare(getSortKey(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Option[]}
|
||||
*/
|
||||
|
||||
visibleOptions(cmd) {
|
||||
const visibleOptions = cmd.options.filter((option) => !option.hidden);
|
||||
// Built-in help option.
|
||||
const helpOption = cmd._getHelpOption();
|
||||
if (helpOption && !helpOption.hidden) {
|
||||
// Automatically hide conflicting flags. Bit dubious but a historical behaviour that is convenient for single-command programs.
|
||||
const removeShort = helpOption.short && cmd._findOption(helpOption.short);
|
||||
const removeLong = helpOption.long && cmd._findOption(helpOption.long);
|
||||
if (!removeShort && !removeLong) {
|
||||
visibleOptions.push(helpOption); // no changes needed
|
||||
} else if (helpOption.long && !removeLong) {
|
||||
visibleOptions.push(
|
||||
cmd.createOption(helpOption.long, helpOption.description),
|
||||
);
|
||||
} else if (helpOption.short && !removeShort) {
|
||||
visibleOptions.push(
|
||||
cmd.createOption(helpOption.short, helpOption.description),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.sortOptions) {
|
||||
visibleOptions.sort(this.compareOptions);
|
||||
}
|
||||
return visibleOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the visible global options. (Not including help.)
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Option[]}
|
||||
*/
|
||||
|
||||
visibleGlobalOptions(cmd) {
|
||||
if (!this.showGlobalOptions) return [];
|
||||
|
||||
const globalOptions = [];
|
||||
for (
|
||||
let ancestorCmd = cmd.parent;
|
||||
ancestorCmd;
|
||||
ancestorCmd = ancestorCmd.parent
|
||||
) {
|
||||
const visibleOptions = ancestorCmd.options.filter(
|
||||
(option) => !option.hidden,
|
||||
);
|
||||
globalOptions.push(...visibleOptions);
|
||||
}
|
||||
if (this.sortOptions) {
|
||||
globalOptions.sort(this.compareOptions);
|
||||
}
|
||||
return globalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of the arguments if any have a description.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {Argument[]}
|
||||
*/
|
||||
|
||||
visibleArguments(cmd) {
|
||||
// Side effect! Apply the legacy descriptions before the arguments are displayed.
|
||||
if (cmd._argsDescription) {
|
||||
cmd.registeredArguments.forEach((argument) => {
|
||||
argument.description =
|
||||
argument.description || cmd._argsDescription[argument.name()] || '';
|
||||
});
|
||||
}
|
||||
|
||||
// If there are any arguments with a description then return all the arguments.
|
||||
if (cmd.registeredArguments.find((argument) => argument.description)) {
|
||||
return cmd.registeredArguments;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command term to show in the list of subcommands.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
subcommandTerm(cmd) {
|
||||
// Legacy. Ignores custom usage string, and nested commands.
|
||||
const args = cmd.registeredArguments
|
||||
.map((arg) => humanReadableArgName(arg))
|
||||
.join(' ');
|
||||
return (
|
||||
cmd._name +
|
||||
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
|
||||
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
|
||||
(args ? ' ' + args : '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option term to show in the list of options.
|
||||
*
|
||||
* @param {Option} option
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
optionTerm(option) {
|
||||
return option.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument term to show in the list of arguments.
|
||||
*
|
||||
* @param {Argument} argument
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
argumentTerm(argument) {
|
||||
return argument.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest command term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestSubcommandTermLength(cmd, helper) {
|
||||
return helper.visibleCommands(cmd).reduce((max, command) => {
|
||||
return Math.max(
|
||||
max,
|
||||
this.displayWidth(
|
||||
helper.styleSubcommandTerm(helper.subcommandTerm(command)),
|
||||
),
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest option term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestOptionTermLength(cmd, helper) {
|
||||
return helper.visibleOptions(cmd).reduce((max, option) => {
|
||||
return Math.max(
|
||||
max,
|
||||
this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest global option term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestGlobalOptionTermLength(cmd, helper) {
|
||||
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
||||
return Math.max(
|
||||
max,
|
||||
this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longest argument term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
longestArgumentTermLength(cmd, helper) {
|
||||
return helper.visibleArguments(cmd).reduce((max, argument) => {
|
||||
return Math.max(
|
||||
max,
|
||||
this.displayWidth(
|
||||
helper.styleArgumentTerm(helper.argumentTerm(argument)),
|
||||
),
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command usage to be displayed at the top of the built-in help.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
commandUsage(cmd) {
|
||||
// Usage
|
||||
let cmdName = cmd._name;
|
||||
if (cmd._aliases[0]) {
|
||||
cmdName = cmdName + '|' + cmd._aliases[0];
|
||||
}
|
||||
let ancestorCmdNames = '';
|
||||
for (
|
||||
let ancestorCmd = cmd.parent;
|
||||
ancestorCmd;
|
||||
ancestorCmd = ancestorCmd.parent
|
||||
) {
|
||||
ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames;
|
||||
}
|
||||
return ancestorCmdNames + cmdName + ' ' + cmd.usage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the command.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
commandDescription(cmd) {
|
||||
// @ts-ignore: because overloaded return type
|
||||
return cmd.description();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subcommand summary to show in the list of subcommands.
|
||||
* (Fallback to description for backwards compatibility.)
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
subcommandDescription(cmd) {
|
||||
// @ts-ignore: because overloaded return type
|
||||
return cmd.summary() || cmd.description();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option description to show in the list of options.
|
||||
*
|
||||
* @param {Option} option
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
optionDescription(option) {
|
||||
const extraInfo = [];
|
||||
|
||||
if (option.argChoices) {
|
||||
extraInfo.push(
|
||||
// use stringify to match the display of the default value
|
||||
`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
|
||||
);
|
||||
}
|
||||
if (option.defaultValue !== undefined) {
|
||||
// default for boolean and negated more for programmer than end user,
|
||||
// but show true/false for boolean option as may be for hand-rolled env or config processing.
|
||||
const showDefault =
|
||||
option.required ||
|
||||
option.optional ||
|
||||
(option.isBoolean() && typeof option.defaultValue === 'boolean');
|
||||
if (showDefault) {
|
||||
extraInfo.push(
|
||||
`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// preset for boolean and negated are more for programmer than end user
|
||||
if (option.presetArg !== undefined && option.optional) {
|
||||
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
|
||||
}
|
||||
if (option.envVar !== undefined) {
|
||||
extraInfo.push(`env: ${option.envVar}`);
|
||||
}
|
||||
if (extraInfo.length > 0) {
|
||||
const extraDescription = `(${extraInfo.join(', ')})`;
|
||||
if (option.description) {
|
||||
return `${option.description} ${extraDescription}`;
|
||||
}
|
||||
return extraDescription;
|
||||
}
|
||||
|
||||
return option.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the argument description to show in the list of arguments.
|
||||
*
|
||||
* @param {Argument} argument
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
argumentDescription(argument) {
|
||||
const extraInfo = [];
|
||||
if (argument.argChoices) {
|
||||
extraInfo.push(
|
||||
// use stringify to match the display of the default value
|
||||
`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`,
|
||||
);
|
||||
}
|
||||
if (argument.defaultValue !== undefined) {
|
||||
extraInfo.push(
|
||||
`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`,
|
||||
);
|
||||
}
|
||||
if (extraInfo.length > 0) {
|
||||
const extraDescription = `(${extraInfo.join(', ')})`;
|
||||
if (argument.description) {
|
||||
return `${argument.description} ${extraDescription}`;
|
||||
}
|
||||
return extraDescription;
|
||||
}
|
||||
return argument.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a list of items, given a heading and an array of formatted items.
|
||||
*
|
||||
* @param {string} heading
|
||||
* @param {string[]} items
|
||||
* @param {Help} helper
|
||||
* @returns string[]
|
||||
*/
|
||||
formatItemList(heading, items, helper) {
|
||||
if (items.length === 0) return [];
|
||||
|
||||
return [helper.styleTitle(heading), ...items, ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* Group items by their help group heading.
|
||||
*
|
||||
* @param {Command[] | Option[]} unsortedItems
|
||||
* @param {Command[] | Option[]} visibleItems
|
||||
* @param {Function} getGroup
|
||||
* @returns {Map<string, Command[] | Option[]>}
|
||||
*/
|
||||
groupItems(unsortedItems, visibleItems, getGroup) {
|
||||
const result = new Map();
|
||||
// Add groups in order of appearance in unsortedItems.
|
||||
unsortedItems.forEach((item) => {
|
||||
const group = getGroup(item);
|
||||
if (!result.has(group)) result.set(group, []);
|
||||
});
|
||||
// Add items in order of appearance in visibleItems.
|
||||
visibleItems.forEach((item) => {
|
||||
const group = getGroup(item);
|
||||
if (!result.has(group)) {
|
||||
result.set(group, []);
|
||||
}
|
||||
result.get(group).push(item);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the built-in help text.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
formatHelp(cmd, helper) {
|
||||
const termWidth = helper.padWidth(cmd, helper);
|
||||
const helpWidth = helper.helpWidth ?? 80; // in case prepareContext() was not called
|
||||
|
||||
function callFormatItem(term, description) {
|
||||
return helper.formatItem(term, termWidth, description, helper);
|
||||
}
|
||||
|
||||
// Usage
|
||||
let output = [
|
||||
`${helper.styleTitle('Usage:')} ${helper.styleUsage(helper.commandUsage(cmd))}`,
|
||||
'',
|
||||
];
|
||||
|
||||
// Description
|
||||
const commandDescription = helper.commandDescription(cmd);
|
||||
if (commandDescription.length > 0) {
|
||||
output = output.concat([
|
||||
helper.boxWrap(
|
||||
helper.styleCommandDescription(commandDescription),
|
||||
helpWidth,
|
||||
),
|
||||
'',
|
||||
]);
|
||||
}
|
||||
|
||||
// Arguments
|
||||
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
||||
return callFormatItem(
|
||||
helper.styleArgumentTerm(helper.argumentTerm(argument)),
|
||||
helper.styleArgumentDescription(helper.argumentDescription(argument)),
|
||||
);
|
||||
});
|
||||
output = output.concat(
|
||||
this.formatItemList('Arguments:', argumentList, helper),
|
||||
);
|
||||
|
||||
// Options
|
||||
const optionGroups = this.groupItems(
|
||||
cmd.options,
|
||||
helper.visibleOptions(cmd),
|
||||
(option) => option.helpGroupHeading ?? 'Options:',
|
||||
);
|
||||
optionGroups.forEach((options, group) => {
|
||||
const optionList = options.map((option) => {
|
||||
return callFormatItem(
|
||||
helper.styleOptionTerm(helper.optionTerm(option)),
|
||||
helper.styleOptionDescription(helper.optionDescription(option)),
|
||||
);
|
||||
});
|
||||
output = output.concat(this.formatItemList(group, optionList, helper));
|
||||
});
|
||||
|
||||
if (helper.showGlobalOptions) {
|
||||
const globalOptionList = helper
|
||||
.visibleGlobalOptions(cmd)
|
||||
.map((option) => {
|
||||
return callFormatItem(
|
||||
helper.styleOptionTerm(helper.optionTerm(option)),
|
||||
helper.styleOptionDescription(helper.optionDescription(option)),
|
||||
);
|
||||
});
|
||||
output = output.concat(
|
||||
this.formatItemList('Global Options:', globalOptionList, helper),
|
||||
);
|
||||
}
|
||||
|
||||
// Commands
|
||||
const commandGroups = this.groupItems(
|
||||
cmd.commands,
|
||||
helper.visibleCommands(cmd),
|
||||
(sub) => sub.helpGroup() || 'Commands:',
|
||||
);
|
||||
commandGroups.forEach((commands, group) => {
|
||||
const commandList = commands.map((sub) => {
|
||||
return callFormatItem(
|
||||
helper.styleSubcommandTerm(helper.subcommandTerm(sub)),
|
||||
helper.styleSubcommandDescription(helper.subcommandDescription(sub)),
|
||||
);
|
||||
});
|
||||
output = output.concat(this.formatItemList(group, commandList, helper));
|
||||
});
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {number}
|
||||
*/
|
||||
displayWidth(str) {
|
||||
return stripColor(str).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
styleTitle(str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
styleUsage(str) {
|
||||
// Usage has lots of parts the user might like to color separately! Assume default usage string which is formed like:
|
||||
// command subcommand [options] [command] <foo> [bar]
|
||||
return str
|
||||
.split(' ')
|
||||
.map((word) => {
|
||||
if (word === '[options]') return this.styleOptionText(word);
|
||||
if (word === '[command]') return this.styleSubcommandText(word);
|
||||
if (word[0] === '[' || word[0] === '<')
|
||||
return this.styleArgumentText(word);
|
||||
return this.styleCommandText(word); // Restrict to initial words?
|
||||
})
|
||||
.join(' ');
|
||||
}
|
||||
styleCommandDescription(str) {
|
||||
return this.styleDescriptionText(str);
|
||||
}
|
||||
styleOptionDescription(str) {
|
||||
return this.styleDescriptionText(str);
|
||||
}
|
||||
styleSubcommandDescription(str) {
|
||||
return this.styleDescriptionText(str);
|
||||
}
|
||||
styleArgumentDescription(str) {
|
||||
return this.styleDescriptionText(str);
|
||||
}
|
||||
styleDescriptionText(str) {
|
||||
return str;
|
||||
}
|
||||
styleOptionTerm(str) {
|
||||
return this.styleOptionText(str);
|
||||
}
|
||||
styleSubcommandTerm(str) {
|
||||
// This is very like usage with lots of parts! Assume default string which is formed like:
|
||||
// subcommand [options] <foo> [bar]
|
||||
return str
|
||||
.split(' ')
|
||||
.map((word) => {
|
||||
if (word === '[options]') return this.styleOptionText(word);
|
||||
if (word[0] === '[' || word[0] === '<')
|
||||
return this.styleArgumentText(word);
|
||||
return this.styleSubcommandText(word); // Restrict to initial words?
|
||||
})
|
||||
.join(' ');
|
||||
}
|
||||
styleArgumentTerm(str) {
|
||||
return this.styleArgumentText(str);
|
||||
}
|
||||
styleOptionText(str) {
|
||||
return str;
|
||||
}
|
||||
styleArgumentText(str) {
|
||||
return str;
|
||||
}
|
||||
styleSubcommandText(str) {
|
||||
return str;
|
||||
}
|
||||
styleCommandText(str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pad width from the maximum term length.
|
||||
*
|
||||
* @param {Command} cmd
|
||||
* @param {Help} helper
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
padWidth(cmd, helper) {
|
||||
return Math.max(
|
||||
helper.longestOptionTermLength(cmd, helper),
|
||||
helper.longestGlobalOptionTermLength(cmd, helper),
|
||||
helper.longestSubcommandTermLength(cmd, helper),
|
||||
helper.longestArgumentTermLength(cmd, helper),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect manually wrapped and indented strings by checking for line break followed by whitespace.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {boolean}
|
||||
*/
|
||||
preformatted(str) {
|
||||
return /\n[^\S\r\n]/.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
|
||||
*
|
||||
* So "TTT", 5, "DDD DDDD DD DDD" might be formatted for this.helpWidth=17 like so:
|
||||
* TTT DDD DDDD
|
||||
* DD DDD
|
||||
*
|
||||
* @param {string} term
|
||||
* @param {number} termWidth
|
||||
* @param {string} description
|
||||
* @param {Help} helper
|
||||
* @returns {string}
|
||||
*/
|
||||
formatItem(term, termWidth, description, helper) {
|
||||
const itemIndent = 2;
|
||||
const itemIndentStr = ' '.repeat(itemIndent);
|
||||
if (!description) return itemIndentStr + term;
|
||||
|
||||
// Pad the term out to a consistent width, so descriptions are aligned.
|
||||
const paddedTerm = term.padEnd(
|
||||
termWidth + term.length - helper.displayWidth(term),
|
||||
);
|
||||
|
||||
// Format the description.
|
||||
const spacerWidth = 2; // between term and description
|
||||
const helpWidth = this.helpWidth ?? 80; // in case prepareContext() was not called
|
||||
const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
|
||||
let formattedDescription;
|
||||
if (
|
||||
remainingWidth < this.minWidthToWrap ||
|
||||
helper.preformatted(description)
|
||||
) {
|
||||
formattedDescription = description;
|
||||
} else {
|
||||
const wrappedDescription = helper.boxWrap(description, remainingWidth);
|
||||
formattedDescription = wrappedDescription.replace(
|
||||
/\n/g,
|
||||
'\n' + ' '.repeat(termWidth + spacerWidth),
|
||||
);
|
||||
}
|
||||
|
||||
// Construct and overall indent.
|
||||
return (
|
||||
itemIndentStr +
|
||||
paddedTerm +
|
||||
' '.repeat(spacerWidth) +
|
||||
formattedDescription.replace(/\n/g, `\n${itemIndentStr}`)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a string at whitespace, preserving existing line breaks.
|
||||
* Wrapping is skipped if the width is less than `minWidthToWrap`.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {number} width
|
||||
* @returns {string}
|
||||
*/
|
||||
boxWrap(str, width) {
|
||||
if (width < this.minWidthToWrap) return str;
|
||||
|
||||
const rawLines = str.split(/\r\n|\n/);
|
||||
// split up text by whitespace
|
||||
const chunkPattern = /[\s]*[^\s]+/g;
|
||||
const wrappedLines = [];
|
||||
rawLines.forEach((line) => {
|
||||
const chunks = line.match(chunkPattern);
|
||||
if (chunks === null) {
|
||||
wrappedLines.push('');
|
||||
return;
|
||||
}
|
||||
|
||||
let sumChunks = [chunks.shift()];
|
||||
let sumWidth = this.displayWidth(sumChunks[0]);
|
||||
chunks.forEach((chunk) => {
|
||||
const visibleWidth = this.displayWidth(chunk);
|
||||
// Accumulate chunks while they fit into width.
|
||||
if (sumWidth + visibleWidth <= width) {
|
||||
sumChunks.push(chunk);
|
||||
sumWidth += visibleWidth;
|
||||
return;
|
||||
}
|
||||
wrappedLines.push(sumChunks.join(''));
|
||||
|
||||
const nextChunk = chunk.trimStart(); // trim space at line break
|
||||
sumChunks = [nextChunk];
|
||||
sumWidth = this.displayWidth(nextChunk);
|
||||
});
|
||||
wrappedLines.push(sumChunks.join(''));
|
||||
});
|
||||
|
||||
return wrappedLines.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip style ANSI escape sequences from the string. In particular, SGR (Select Graphic Rendition) codes.
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
* @package
|
||||
*/
|
||||
|
||||
function stripColor(str) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
|
||||
return str.replace(sgrPattern, '');
|
||||
}
|
||||
|
||||
exports.Help = Help;
|
||||
exports.stripColor = stripColor;
|
||||
@@ -0,0 +1,287 @@
|
||||
import { SyntaxKind } from "../../ast/index.js";
|
||||
import { getNodeCommonData, getNodeDataType, } from "./encoder.generated.js";
|
||||
import { MsgpackWriter } from "./msgpack.js";
|
||||
import { childProperties, HEADER_OFFSET_EXTENDED_DATA, HEADER_OFFSET_METADATA, HEADER_OFFSET_NODES, HEADER_OFFSET_STRING_TABLE, HEADER_OFFSET_STRING_TABLE_OFFSETS, HEADER_OFFSET_STRUCTURED_DATA, HEADER_SIZE, KIND_NODE_LIST, NODE_DATA_TYPE_CHILDREN, NODE_DATA_TYPE_EXTENDED, NODE_DATA_TYPE_STRING, NODE_LEN, PROTOCOL_VERSION, } from "./protocol.js";
|
||||
const NODE_FIELDS = NODE_LEN / 4;
|
||||
const NODE_FIELD_NEXT = 3;
|
||||
const NO_STRUCTURED_DATA = 0xFFFFFFFF;
|
||||
// String table that accumulates strings into a flat byte pool.
|
||||
class StringTable {
|
||||
parts;
|
||||
byteLen;
|
||||
offsets;
|
||||
constructor() {
|
||||
this.parts = [];
|
||||
this.byteLen = 0;
|
||||
this.offsets = [];
|
||||
}
|
||||
add(text) {
|
||||
const index = this.offsets.length;
|
||||
const encoder = cachedEncoder();
|
||||
const encodedLength = encoder.encode(text).length;
|
||||
const offset = this.byteLen;
|
||||
this.parts.push(text);
|
||||
this.byteLen += encodedLength;
|
||||
this.offsets.push(offset, offset + encodedLength);
|
||||
return index;
|
||||
}
|
||||
encode() {
|
||||
const encoder = cachedEncoder();
|
||||
const dataBytes = encoder.encode(this.parts.join(""));
|
||||
const offsetBytes = new Uint8Array(this.offsets.length * 4);
|
||||
const view = new DataView(offsetBytes.buffer);
|
||||
for (let i = 0; i < this.offsets.length; i++) {
|
||||
view.setUint32(i * 4, this.offsets[i], true);
|
||||
}
|
||||
const result = new Uint8Array(offsetBytes.length + dataBytes.length);
|
||||
result.set(offsetBytes, 0);
|
||||
result.set(dataBytes, offsetBytes.length);
|
||||
return result;
|
||||
}
|
||||
stringByteLength() {
|
||||
return this.byteLen;
|
||||
}
|
||||
offsetsCount() {
|
||||
return this.offsets.length;
|
||||
}
|
||||
}
|
||||
let _encoder;
|
||||
function cachedEncoder() {
|
||||
return _encoder ??= new TextEncoder();
|
||||
}
|
||||
function getChildrenPropertyMask(node) {
|
||||
const kind = node.kind;
|
||||
const props = childProperties[kind];
|
||||
if (!props) {
|
||||
return 0;
|
||||
}
|
||||
const n = node;
|
||||
let mask = 0;
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
const prop = props[i];
|
||||
if (prop !== undefined && isChildPresent(n[prop])) {
|
||||
mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
// A child is "present" if it's non-null/non-undefined.
|
||||
// This matches the Go encoder's behavior where non-nil NodeLists (even empty)
|
||||
// are treated as present, and only nil NodeLists are absent.
|
||||
function isChildPresent(v) {
|
||||
if (v === undefined || v === null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
function recordNodeStrings(node, strs) {
|
||||
return strs.add(node.text ?? "");
|
||||
}
|
||||
function encodeFileReferences(refs, writer) {
|
||||
if (!refs || refs.length === 0)
|
||||
return NO_STRUCTURED_DATA;
|
||||
const offset = writer.finish().length;
|
||||
writer.writeArrayHeader(refs.length);
|
||||
for (const ref of refs) {
|
||||
writer.writeArrayHeader(5);
|
||||
writer.writeUint(ref.pos);
|
||||
writer.writeUint(ref.end);
|
||||
writer.writeString(ref.fileName);
|
||||
writer.writeUint(ref.resolutionMode ?? 0);
|
||||
writer.writeBool(ref.preserve ?? false);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
function recordExtendedData(node, strs, extendedData, structuredWriter) {
|
||||
const offset = extendedData.length * 4;
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
const sf = node;
|
||||
const textIndex = strs.add(sf.text);
|
||||
const fileNameIndex = strs.add(sf.fileName);
|
||||
const pathIndex = strs.add(sf.path);
|
||||
const referencedFilesOffset = encodeFileReferences(sf.referencedFiles, structuredWriter);
|
||||
const typeRefDirectivesOffset = encodeFileReferences(sf.typeReferenceDirectives, structuredWriter);
|
||||
const libRefDirectivesOffset = encodeFileReferences(sf.libReferenceDirectives, structuredWriter);
|
||||
extendedData.push(textIndex, fileNameIndex, pathIndex, sf.languageVariant, sf.scriptKind, referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, 0);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.TemplateHead ||
|
||||
node.kind === SyntaxKind.TemplateMiddle ||
|
||||
node.kind === SyntaxKind.TemplateTail) {
|
||||
const tmpl = node;
|
||||
const text = tmpl.text ?? "";
|
||||
const rawText = tmpl.rawText ?? "";
|
||||
const templateFlags = tmpl.templateFlags ?? 0;
|
||||
const textIndex = strs.add(text);
|
||||
const rawTextIndex = strs.add(rawText);
|
||||
extendedData.push(textIndex, rawTextIndex, templateFlags);
|
||||
}
|
||||
else {
|
||||
// StringLiteral, NumericLiteral, BigIntLiteral, RegularExpressionLiteral,
|
||||
// NoSubstitutionTemplateLiteral — format: [textIndex, tokenFlags]
|
||||
const n = node;
|
||||
const text = n.text ?? "";
|
||||
const tokenFlags = n.tokenFlags ?? 0;
|
||||
const textIndex = strs.add(text);
|
||||
extendedData.push(textIndex, tokenFlags);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
function getNodeData(node, strs, extendedData, structuredWriter) {
|
||||
const t = getNodeDataType(node.kind);
|
||||
const common = getNodeCommonData(node);
|
||||
switch (t) {
|
||||
case NODE_DATA_TYPE_CHILDREN:
|
||||
return t | common | getChildrenPropertyMask(node);
|
||||
case NODE_DATA_TYPE_STRING:
|
||||
return t | common | recordNodeStrings(node, strs);
|
||||
case NODE_DATA_TYPE_EXTENDED:
|
||||
return t | common | recordExtendedData(node, strs, extendedData, structuredWriter);
|
||||
default:
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
}
|
||||
function getChildPropertiesForNode(node) {
|
||||
return childProperties[node.kind];
|
||||
}
|
||||
// Returns whether a value is a NodeArray (array-like with pos and end).
|
||||
function isNodeArray(value) {
|
||||
return Array.isArray(value) && typeof value.pos === "number" && typeof value.end === "number";
|
||||
}
|
||||
/**
|
||||
* Encode a SourceFile AST node into the binary format.
|
||||
*/
|
||||
export function encodeSourceFile(sourceFile) {
|
||||
return encodeNode(sourceFile);
|
||||
}
|
||||
/**
|
||||
* Encode an arbitrary AST node into the binary format.
|
||||
* When encoding a non-SourceFile node, the header hash and parse options fields will be zero.
|
||||
*/
|
||||
export function encodeNode(node) {
|
||||
const strs = new StringTable();
|
||||
const extendedDataValues = [];
|
||||
const structuredWriter = new MsgpackWriter();
|
||||
// We'll build an array of uint32 values for the nodes section, 7 per node
|
||||
const nodeValues = [];
|
||||
// Nil node (index 0)
|
||||
nodeValues.push(0, 0, 0, 0, 0, 0, 0);
|
||||
let nodeCount = 0;
|
||||
let parentIndex = 0;
|
||||
let prevIndex = 0;
|
||||
function visitNode(node) {
|
||||
nodeCount++;
|
||||
const currentIndex = nodeCount;
|
||||
if (prevIndex !== 0) {
|
||||
// Set next pointer on previous sibling
|
||||
nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex;
|
||||
}
|
||||
const data = getNodeData(node, strs, extendedDataValues, structuredWriter);
|
||||
nodeValues.push(node.kind, node.pos >= 0 ? node.pos : 0, node.end >= 0 ? node.end : 0, 0, // next (filled in later)
|
||||
parentIndex, data, node.flags);
|
||||
const saveParentIndex = parentIndex;
|
||||
const savePrevIndex = prevIndex;
|
||||
parentIndex = currentIndex;
|
||||
prevIndex = 0;
|
||||
visitChildren(node);
|
||||
prevIndex = currentIndex;
|
||||
parentIndex = saveParentIndex;
|
||||
}
|
||||
function visitNodeList(list) {
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
nodeCount++;
|
||||
const currentIndex = nodeCount;
|
||||
if (prevIndex !== 0) {
|
||||
nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex;
|
||||
}
|
||||
nodeValues.push(KIND_NODE_LIST, list.pos >= 0 ? list.pos : 0, list.end >= 0 ? list.end : 0, 0, // next
|
||||
parentIndex, list.length, // data for NodeList is its length
|
||||
0);
|
||||
const saveParentIndex = parentIndex;
|
||||
parentIndex = currentIndex;
|
||||
prevIndex = 0;
|
||||
for (const child of list) {
|
||||
visitNode(child);
|
||||
}
|
||||
prevIndex = currentIndex;
|
||||
parentIndex = saveParentIndex;
|
||||
}
|
||||
function visitChildren(node) {
|
||||
const props = getChildPropertiesForNode(node);
|
||||
const n = node;
|
||||
if (props) {
|
||||
for (const propName of props) {
|
||||
if (propName === undefined)
|
||||
continue;
|
||||
const child = n[propName];
|
||||
if (child === undefined || child === null)
|
||||
continue;
|
||||
if (isNodeArray(child)) {
|
||||
visitNodeList(child);
|
||||
}
|
||||
else {
|
||||
visitNode(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Encode root node
|
||||
nodeCount++;
|
||||
parentIndex++;
|
||||
const rootData = getNodeData(node, strs, extendedDataValues, structuredWriter);
|
||||
nodeValues.push(node.kind, node.pos >= 0 ? node.pos : 0, node.end >= 0 ? node.end : 0, 0, 0, rootData, node.flags);
|
||||
const saveParent = parentIndex;
|
||||
prevIndex = 0;
|
||||
parentIndex = 1; // root is at index 1
|
||||
visitChildren(node);
|
||||
parentIndex = saveParent;
|
||||
// Encode extended data section
|
||||
const extendedDataBytes = new Uint8Array(extendedDataValues.length * 4);
|
||||
const extView = new DataView(extendedDataBytes.buffer);
|
||||
for (let i = 0; i < extendedDataValues.length; i++) {
|
||||
extView.setUint32(i * 4, extendedDataValues[i], true);
|
||||
}
|
||||
// Encode structured data section
|
||||
const structuredDataBytes = structuredWriter.finish();
|
||||
// Encode string table
|
||||
const strsBytes = strs.encode();
|
||||
// Encode nodes section
|
||||
const nodesBytes = new Uint8Array(nodeValues.length * 4);
|
||||
const nodesView = new DataView(nodesBytes.buffer);
|
||||
for (let i = 0; i < nodeValues.length; i++) {
|
||||
nodesView.setUint32(i * 4, nodeValues[i] >>> 0, true);
|
||||
}
|
||||
// Calculate section offsets
|
||||
const offsetStringTableOffsets = HEADER_SIZE;
|
||||
const offsetStringTableData = HEADER_SIZE + strs.offsetsCount() * 4;
|
||||
const offsetExtendedData = offsetStringTableData + strs.stringByteLength();
|
||||
const offsetStructuredData = offsetExtendedData + extendedDataBytes.length;
|
||||
const offsetNodes = offsetStructuredData + structuredDataBytes.length;
|
||||
// Build header
|
||||
const header = new Uint8Array(HEADER_SIZE);
|
||||
const headerView = new DataView(header.buffer);
|
||||
const metadata = PROTOCOL_VERSION << 24;
|
||||
headerView.setUint32(HEADER_OFFSET_METADATA, metadata, true);
|
||||
// bytes 4-19: hash (zero for non-SourceFile, we don't have access to xxh3 here)
|
||||
// byte 20-23: parse options (zero for non-SourceFile)
|
||||
headerView.setUint32(HEADER_OFFSET_STRING_TABLE_OFFSETS, offsetStringTableOffsets, true);
|
||||
headerView.setUint32(HEADER_OFFSET_STRING_TABLE, offsetStringTableData, true);
|
||||
headerView.setUint32(HEADER_OFFSET_EXTENDED_DATA, offsetExtendedData, true);
|
||||
headerView.setUint32(HEADER_OFFSET_STRUCTURED_DATA, offsetStructuredData, true);
|
||||
headerView.setUint32(HEADER_OFFSET_NODES, offsetNodes, true);
|
||||
// Concatenate all sections
|
||||
const result = new Uint8Array(header.length + strsBytes.length + extendedDataBytes.length + structuredDataBytes.length + nodesBytes.length);
|
||||
result.set(header, 0);
|
||||
result.set(strsBytes, HEADER_SIZE);
|
||||
result.set(extendedDataBytes, offsetExtendedData);
|
||||
result.set(structuredDataBytes, offsetStructuredData);
|
||||
result.set(nodesBytes, offsetNodes);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Encode a Uint8Array to a base64 string.
|
||||
*/
|
||||
export function uint8ArrayToBase64(data) {
|
||||
return Buffer.from(data).toString("base64");
|
||||
}
|
||||
//# sourceMappingURL=encoder.js.map
|
||||
@@ -0,0 +1,227 @@
|
||||
import { expect, expectTypeOf, test } from "vitest";
|
||||
|
||||
import * as z from "zod/v4";
|
||||
|
||||
test("opt passthrough", () => {
|
||||
const object = z.object({
|
||||
a: z.lazy(() => z.string()),
|
||||
b: z.lazy(() => z.string().optional()),
|
||||
c: z.lazy(() => z.string().default("default")),
|
||||
});
|
||||
|
||||
type ObjectTypeIn = z.input<typeof object>;
|
||||
expectTypeOf<ObjectTypeIn>().toEqualTypeOf<{
|
||||
a: string;
|
||||
b?: string | undefined;
|
||||
c?: string | undefined;
|
||||
}>();
|
||||
|
||||
type ObjectTypeOut = z.output<typeof object>;
|
||||
expectTypeOf<ObjectTypeOut>().toEqualTypeOf<{
|
||||
a: string;
|
||||
b?: string | undefined;
|
||||
c: string;
|
||||
}>();
|
||||
|
||||
const result = object.parse(
|
||||
{
|
||||
a: "hello",
|
||||
b: undefined,
|
||||
},
|
||||
{ jitless: true }
|
||||
);
|
||||
expect(result).toEqual({
|
||||
a: "hello",
|
||||
// b: undefined,
|
||||
c: "default",
|
||||
});
|
||||
|
||||
expect(z.lazy(() => z.string())._zod.optin).toEqual(undefined);
|
||||
expect(z.lazy(() => z.string())._zod.optout).toEqual(undefined);
|
||||
|
||||
expect(z.lazy(() => z.string().optional())._zod.optin).toEqual("optional");
|
||||
expect(z.lazy(() => z.string().optional())._zod.optout).toEqual("optional");
|
||||
|
||||
expect(z.lazy(() => z.string().default("asdf"))._zod.optin).toEqual("optional");
|
||||
expect(z.lazy(() => z.string().default("asdf"))._zod.optout).toEqual(undefined);
|
||||
});
|
||||
|
||||
////////////// LAZY //////////////
|
||||
|
||||
test("schema getter", () => {
|
||||
z.lazy(() => z.string()).parse("asdf");
|
||||
});
|
||||
|
||||
test("lazy proxy", () => {
|
||||
const schema = z.lazy(() => z.string())._zod.innerType.min(6);
|
||||
schema.parse("123456");
|
||||
expect(schema.safeParse("12345").success).toBe(false);
|
||||
});
|
||||
|
||||
interface Category {
|
||||
name: string;
|
||||
subcategories: Category[];
|
||||
}
|
||||
|
||||
const testCategory: Category = {
|
||||
name: "I",
|
||||
subcategories: [
|
||||
{
|
||||
name: "A",
|
||||
subcategories: [
|
||||
{
|
||||
name: "1",
|
||||
subcategories: [
|
||||
{
|
||||
name: "a",
|
||||
subcategories: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
test("recursion with z.lazy", () => {
|
||||
const Category: z.ZodType<Category> = z.lazy(() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
subcategories: z.array(Category),
|
||||
})
|
||||
);
|
||||
Category.parse(testCategory);
|
||||
});
|
||||
|
||||
type LinkedList = null | { value: number; next: LinkedList };
|
||||
|
||||
const linkedListExample = {
|
||||
value: 1,
|
||||
next: {
|
||||
value: 2,
|
||||
next: {
|
||||
value: 3,
|
||||
next: {
|
||||
value: 4,
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test("recursive union wit z.lazy", () => {
|
||||
const LinkedListSchema: z.ZodType<LinkedList> = z.lazy(() =>
|
||||
z.union([
|
||||
z.null(),
|
||||
z.object({
|
||||
value: z.number(),
|
||||
next: LinkedListSchema,
|
||||
}),
|
||||
])
|
||||
);
|
||||
LinkedListSchema.parse(linkedListExample);
|
||||
});
|
||||
|
||||
interface A {
|
||||
val: number;
|
||||
b: B;
|
||||
}
|
||||
|
||||
interface B {
|
||||
val: number;
|
||||
a?: A | undefined;
|
||||
}
|
||||
|
||||
test("mutual recursion with lazy", () => {
|
||||
const Alazy: z.ZodType<A> = z.lazy(() =>
|
||||
z.object({
|
||||
val: z.number(),
|
||||
b: Blazy,
|
||||
})
|
||||
);
|
||||
|
||||
const Blazy: z.ZodType<B> = z.lazy(() =>
|
||||
z.object({
|
||||
val: z.number(),
|
||||
a: Alazy.optional(),
|
||||
})
|
||||
);
|
||||
|
||||
const testData = {
|
||||
val: 1,
|
||||
b: {
|
||||
val: 5,
|
||||
a: {
|
||||
val: 3,
|
||||
b: {
|
||||
val: 4,
|
||||
a: {
|
||||
val: 2,
|
||||
b: {
|
||||
val: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Alazy.parse(testData);
|
||||
Blazy.parse(testData.b);
|
||||
|
||||
expect(() => Alazy.parse({ val: "asdf" })).toThrow();
|
||||
});
|
||||
|
||||
// TODO
|
||||
test("mutual recursion with cyclical data", () => {
|
||||
const a: any = { val: 1 };
|
||||
const b: any = { val: 2 };
|
||||
a.b = b;
|
||||
b.a = a;
|
||||
});
|
||||
|
||||
test("complicated self-recursion", () => {
|
||||
const Category = z.object({
|
||||
name: z.string(),
|
||||
age: z.optional(z.number()),
|
||||
get nullself() {
|
||||
return Category.nullable();
|
||||
},
|
||||
get optself() {
|
||||
return Category.optional();
|
||||
},
|
||||
get self() {
|
||||
return Category;
|
||||
},
|
||||
get subcategories() {
|
||||
return z.array(Category);
|
||||
},
|
||||
nested: z.object({
|
||||
get sub() {
|
||||
return Category;
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
type _Category = z.output<typeof Category>;
|
||||
});
|
||||
|
||||
test("lazy initialization", () => {
|
||||
const a: any = z.lazy(() => a).optional();
|
||||
const b: any = z.lazy(() => b).nullable();
|
||||
const c: any = z.lazy(() => c).default({} as any);
|
||||
const d: any = z.lazy(() => d).prefault({} as any);
|
||||
const e: any = z.lazy(() => e).nonoptional();
|
||||
const f: any = z.lazy(() => f).catch({} as any);
|
||||
const g: any = z.lazy(() => z.object({ g })).readonly();
|
||||
|
||||
const baseCategorySchema = z.object({
|
||||
name: z.string(),
|
||||
});
|
||||
type Category = z.infer<typeof baseCategorySchema> & {
|
||||
subcategories: Category[];
|
||||
};
|
||||
const categorySchema: z.ZodType<Category> = baseCategorySchema.extend({
|
||||
subcategories: z.lazy(() => categorySchema.array()),
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
# @babel/runtime
|
||||
|
||||
> babel's modular runtime helpers
|
||||
|
||||
See our website [@babel/runtime](https://babeljs.io/docs/babel-runtime) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save @babel/runtime
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/runtime
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
const {Transform} = require('stream');
|
||||
const withParser = require('./withParser');
|
||||
|
||||
class Batch extends Transform {
|
||||
static make(options) {
|
||||
return new Batch(options);
|
||||
}
|
||||
|
||||
static withParser(options) {
|
||||
return withParser(Batch.make, options);
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
super(Object.assign({}, options, {writableObjectMode: true, readableObjectMode: true}));
|
||||
this._batchSize = 1000;
|
||||
if (options && typeof options.batchSize == 'number' && options.batchSize > 0) {
|
||||
this._batchSize = options.batchSize;
|
||||
}
|
||||
this._accumulator = [];
|
||||
}
|
||||
|
||||
_transform(chunk, _, callback) {
|
||||
this._accumulator.push(chunk);
|
||||
if (this._accumulator.length >= this._batchSize) {
|
||||
this.push(this._accumulator);
|
||||
this._accumulator = [];
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
|
||||
_flush(callback) {
|
||||
if (this._accumulator.length) {
|
||||
this.push(this._accumulator);
|
||||
this._accumulator = null;
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
Batch.batch = Batch.make;
|
||||
Batch.make.Constructor = Batch;
|
||||
|
||||
module.exports = Batch;
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "prelude-ls",
|
||||
"version": "1.2.1",
|
||||
"author": "George Zahariev <z@georgezahariev.com>",
|
||||
"description": "prelude.ls is a functionally oriented utility library. It is powerful and flexible. Almost all of its functions are curried. It is written in, and is the recommended base library for, LiveScript.",
|
||||
"keywords": [
|
||||
"prelude",
|
||||
"livescript",
|
||||
"utility",
|
||||
"ls",
|
||||
"coffeescript",
|
||||
"javascript",
|
||||
"library",
|
||||
"functional",
|
||||
"array",
|
||||
"list",
|
||||
"object",
|
||||
"string"
|
||||
],
|
||||
"main": "lib/",
|
||||
"files": [
|
||||
"lib/",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"homepage": "http://preludels.com",
|
||||
"bugs": "https://github.com/gkz/prelude-ls/issues",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/gkz/prelude-ls.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"livescript": "^1.6.0",
|
||||
"uglify-js": "^3.8.1",
|
||||
"mocha": "^7.1.1",
|
||||
"browserify": "^16.5.1",
|
||||
"sinon": "~8.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.1](https://github.com/humanwhocodes/module-importer/compare/v1.0.0...v1.0.1) (2022-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Ensure CommonJS mode works correctly. ([cf54a0b](https://github.com/humanwhocodes/module-importer/commit/cf54a0b998085066fbe1776dd0b4cacd808cc192)), closes [#6](https://github.com/humanwhocodes/module-importer/issues/6)
|
||||
|
||||
## 1.0.0 (2022-08-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Implement ModuleImporter ([3ce4e82](https://www.github.com/humanwhocodes/module-importer/commit/3ce4e820c30c114e787bfed00a0966ac4772f563))
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"_shortw_utils.js","sourceRoot":"","sources":["../src/_shortw_utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,sEAAsE;AACtE,OAAO,EAAgC,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGtF,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CAAC,IAAW;IACjC,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAKD,gEAAgE;AAChE,MAAM,UAAU,WAAW,CAAC,QAAkB,EAAE,OAAc;IAC5D,MAAM,MAAM,GAAG,CAAC,IAAW,EAAW,EAAE,CAAC,WAAW,CAAC,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
|
||||
@@ -0,0 +1,204 @@
|
||||
# dateformat
|
||||
|
||||
A node.js package for Steven Levithan's excellent [dateFormat()][dateformat] function.
|
||||
|
||||
[](https://travis-ci.org/felixge/node-dateformat)
|
||||
|
||||
## Modifications
|
||||
|
||||
- Removed the `Date.prototype.format` method. Sorry folks, but extending native prototypes is for suckers.
|
||||
- Added a `module.exports = dateFormat;` statement at the bottom
|
||||
- Added the placeholder `N` to get the ISO 8601 numeric representation of the day of the week
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install dateformat
|
||||
$ dateformat --help
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
As taken from Steven's post, modified to match the Modifications listed above:
|
||||
|
||||
```js
|
||||
var dateFormat = require("dateformat");
|
||||
var now = new Date();
|
||||
|
||||
// Basic usage
|
||||
dateFormat(now, "dddd, mmmm dS, yyyy, h:MM:ss TT");
|
||||
// Saturday, June 9th, 2007, 5:46:21 PM
|
||||
|
||||
// You can use one of several named masks
|
||||
dateFormat(now, "isoDateTime");
|
||||
// 2007-06-09T17:46:21
|
||||
|
||||
// ...Or add your own
|
||||
dateFormat.masks.hammerTime = 'HH:MM! "Can\'t touch this!"';
|
||||
dateFormat(now, "hammerTime");
|
||||
// 17:46! Can't touch this!
|
||||
|
||||
// You can also provide the date as a string
|
||||
dateFormat("Jun 9 2007", "fullDate");
|
||||
// Saturday, June 9, 2007
|
||||
|
||||
// Note that if you don't include the mask argument,
|
||||
// dateFormat.masks.default is used
|
||||
dateFormat(now);
|
||||
// Sat Jun 09 2007 17:46:21
|
||||
|
||||
// And if you don't include the date argument,
|
||||
// the current date and time is used
|
||||
dateFormat();
|
||||
// Sat Jun 09 2007 17:46:22
|
||||
|
||||
// You can also skip the date argument (as long as your mask doesn't
|
||||
// contain any numbers), in which case the current date/time is used
|
||||
dateFormat("longTime");
|
||||
// 5:46:22 PM EST
|
||||
|
||||
// And finally, you can convert local time to UTC time. Simply pass in
|
||||
// true as an additional argument (no argument skipping allowed in this case):
|
||||
dateFormat(now, "longTime", true);
|
||||
// 10:46:21 PM UTC
|
||||
|
||||
// ...Or add the prefix "UTC:" or "GMT:" to your mask.
|
||||
dateFormat(now, "UTC:h:MM:ss TT Z");
|
||||
// 10:46:21 PM UTC
|
||||
|
||||
// You can also get the ISO 8601 week of the year:
|
||||
dateFormat(now, "W");
|
||||
// 42
|
||||
|
||||
// and also get the ISO 8601 numeric representation of the day of the week:
|
||||
dateFormat(now, "N");
|
||||
// 6
|
||||
```
|
||||
|
||||
### Mask options
|
||||
|
||||
| Mask | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `d` | Day of the month as digits; no leading zero for single-digit days. |
|
||||
| `dd` | Day of the month as digits; leading zero for single-digit days. |
|
||||
| `ddd` | Day of the week as a three-letter abbreviation. |
|
||||
| `DDD` | "Ysd", "Tdy" or "Tmw" if date lies within these three days. Else fall back to ddd. |
|
||||
| `dddd` | Day of the week as its full name. |
|
||||
| `DDDD` | "Yesterday", "Today" or "Tomorrow" if date lies within these three days. Else fall back to dddd. |
|
||||
| `m` | Month as digits; no leading zero for single-digit months. |
|
||||
| `mm` | Month as digits; leading zero for single-digit months. |
|
||||
| `mmm` | Month as a three-letter abbreviation. |
|
||||
| `mmmm` | Month as its full name. |
|
||||
| `yy` | Year as last two digits; leading zero for years less than 10. |
|
||||
| `yyyy` | Year represented by four digits. |
|
||||
| `h` | Hours; no leading zero for single-digit hours (12-hour clock). |
|
||||
| `hh` | Hours; leading zero for single-digit hours (12-hour clock). |
|
||||
| `H` | Hours; no leading zero for single-digit hours (24-hour clock). |
|
||||
| `HH` | Hours; leading zero for single-digit hours (24-hour clock). |
|
||||
| `M` | Minutes; no leading zero for single-digit minutes. |
|
||||
| `MM` | Minutes; leading zero for single-digit minutes. |
|
||||
| `N` | ISO 8601 numeric representation of the day of the week. |
|
||||
| `o` | GMT/UTC timezone offset, e.g. -0500 or +0230. |
|
||||
| `p` | GMT/UTC timezone offset, e.g. -05:00 or +02:30. |
|
||||
| `s` | Seconds; no leading zero for single-digit seconds. |
|
||||
| `ss` | Seconds; leading zero for single-digit seconds. |
|
||||
| `S` | The date's ordinal suffix (st, nd, rd, or th). Works well with `d`. |
|
||||
| `l` | Milliseconds; gives 3 digits. |
|
||||
| `L` | Milliseconds; gives 2 digits. |
|
||||
| `t` | Lowercase, single-character time marker string: a or p. |
|
||||
| `tt` | Lowercase, two-character time marker string: am or pm. |
|
||||
| `T` | Uppercase, single-character time marker string: A or P. |
|
||||
| `TT` | Uppercase, two-character time marker string: AM or PM. |
|
||||
| `W` | ISO 8601 week number of the year, e.g. 4, 42 |
|
||||
| `WW` | ISO 8601 week number of the year, leading zero for single-digit, e.g. 04, 42 |
|
||||
| `Z` | US timezone abbreviation, e.g. EST or MDT. For non-US timezones, the GMT/UTC offset is returned, e.g. GMT-0500 |
|
||||
| `'...'`, `"..."` | Literal character sequence. Surrounding quotes are removed. |
|
||||
| `UTC:` | Must be the first four characters of the mask. Converts the date from local time to UTC/GMT/Zulu time before applying the mask. The "UTC:" prefix is removed. |
|
||||
|
||||
### Named Formats
|
||||
|
||||
| Name | Mask | Example |
|
||||
| ----------------- | ------------------------------ | ------------------------ |
|
||||
| `default` | `ddd mmm dd yyyy HH:MM:ss` | Sat Jun 09 2007 17:46:21 |
|
||||
| `shortDate` | `m/d/yy` | 6/9/07 |
|
||||
| `paddedShortDate` | `mm/dd/yyyy` | 06/09/2007 |
|
||||
| `mediumDate` | `mmm d, yyyy` | Jun 9, 2007 |
|
||||
| `longDate` | `mmmm d, yyyy` | June 9, 2007 |
|
||||
| `fullDate` | `dddd, mmmm d, yyyy` | Saturday, June 9, 2007 |
|
||||
| `shortTime` | `h:MM TT` | 5:46 PM |
|
||||
| `mediumTime` | `h:MM:ss TT` | 5:46:21 PM |
|
||||
| `longTime` | `h:MM:ss TT Z` | 5:46:21 PM EST |
|
||||
| `isoDate` | `yyyy-mm-dd` | 2007-06-09 |
|
||||
| `isoTime` | `HH:MM:ss` | 17:46:21 |
|
||||
| `isoDateTime` | `yyyy-mm-dd'T'HH:MM:sso` | 2007-06-09T17:46:21+0700 |
|
||||
| `isoUtcDateTime` | `UTC:yyyy-mm-dd'T'HH:MM:ss'Z'` | 2007-06-09T22:46:21Z |
|
||||
|
||||
### Localization
|
||||
|
||||
Day names, month names and the AM/PM indicators can be localized by
|
||||
passing an object with the necessary strings. For example:
|
||||
|
||||
```js
|
||||
var dateFormat = require("dateformat");
|
||||
dateFormat.i18n = {
|
||||
dayNames: [
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
],
|
||||
monthNames: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
timeNames: ["a", "p", "am", "pm", "A", "P", "AM", "PM"],
|
||||
};
|
||||
```
|
||||
|
||||
> Notice that only one language is supported at a time and all strings
|
||||
> _must_ be present in the new value.
|
||||
|
||||
### Breaking change in 2.1.0
|
||||
|
||||
- 2.1.0 was published with a breaking change, for those using localized strings.
|
||||
- 2.2.0 has been published without the change, to keep packages refering to ^2.0.0 to continue working. This is now branch v2_2.
|
||||
- 3.0.\* contains the localized AM/PM change.
|
||||
|
||||
## License
|
||||
|
||||
(c) 2007-2009 Steven Levithan [stevenlevithan.com][stevenlevithan], MIT license.
|
||||
|
||||
[dateformat]: http://blog.stevenlevithan.com/archives/date-time-format
|
||||
[stevenlevithan]: http://stevenlevithan.com/
|
||||
@@ -0,0 +1,27 @@
|
||||
function _importDeferProxy(e) {
|
||||
var t = null,
|
||||
constValue = function constValue(e) {
|
||||
return function () {
|
||||
return e;
|
||||
};
|
||||
},
|
||||
proxy = function proxy(r) {
|
||||
return function (n, o, f) {
|
||||
return null === t && (t = e()), r(t, o, f);
|
||||
};
|
||||
};
|
||||
return new Proxy({}, {
|
||||
defineProperty: constValue(!1),
|
||||
deleteProperty: constValue(!1),
|
||||
get: proxy(Reflect.get),
|
||||
getOwnPropertyDescriptor: proxy(Reflect.getOwnPropertyDescriptor),
|
||||
getPrototypeOf: constValue(null),
|
||||
isExtensible: constValue(!1),
|
||||
has: proxy(Reflect.has),
|
||||
ownKeys: proxy(Reflect.ownKeys),
|
||||
preventExtensions: constValue(!0),
|
||||
set: constValue(!1),
|
||||
setPrototypeOf: constValue(!1)
|
||||
});
|
||||
}
|
||||
module.exports = _importDeferProxy, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { Scope } from '../scope';
|
||||
import type { Variable } from './Variable';
|
||||
import { ESLintScopeVariable } from './ESLintScopeVariable';
|
||||
export interface ImplicitLibVariableOptions {
|
||||
readonly eslintImplicitGlobalSetting?: ESLintScopeVariable['eslintImplicitGlobalSetting'];
|
||||
readonly isTypeVariable?: boolean;
|
||||
readonly isValueVariable?: boolean;
|
||||
readonly writeable?: boolean;
|
||||
}
|
||||
export interface LibDefinition {
|
||||
libs: readonly LibDefinition[];
|
||||
variables: readonly [string, ImplicitLibVariableOptions][];
|
||||
}
|
||||
/**
|
||||
* An variable implicitly defined by the TS Lib
|
||||
*/
|
||||
export declare class ImplicitLibVariable extends ESLintScopeVariable implements Variable {
|
||||
/**
|
||||
* `true` if the variable is valid in a type context, false otherwise
|
||||
*/
|
||||
readonly isTypeVariable: boolean;
|
||||
/**
|
||||
* `true` if the variable is valid in a value context, false otherwise
|
||||
*/
|
||||
readonly isValueVariable: boolean;
|
||||
constructor(scope: Scope, name: string, { eslintImplicitGlobalSetting, isTypeVariable, isValueVariable, writeable, }: ImplicitLibVariableOptions);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABILITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference lib="es2018" />
|
||||
/// <reference lib="dom" />
|
||||
/// <reference lib="webworker.importscripts" />
|
||||
/// <reference lib="scripthost" />
|
||||
/// <reference lib="dom.iterable" />
|
||||
/// <reference lib="dom.asynciterable" />
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = getLevelLabelData
|
||||
const { LEVELS, LEVEL_NAMES } = require('../constants')
|
||||
|
||||
/**
|
||||
* Given initial settings for custom levels/names and use of only custom props
|
||||
* get the level label that corresponds with a given level number
|
||||
*
|
||||
* @param {boolean} useOnlyCustomProps
|
||||
* @param {object} customLevels
|
||||
* @param {object} customLevelNames
|
||||
*
|
||||
* @returns {function} A function that takes a number level and returns the level's label string
|
||||
*/
|
||||
function getLevelLabelData (useOnlyCustomProps, customLevels, customLevelNames) {
|
||||
const levels = useOnlyCustomProps ? customLevels || LEVELS : Object.assign({}, LEVELS, customLevels)
|
||||
const levelNames = useOnlyCustomProps ? customLevelNames || LEVEL_NAMES : Object.assign({}, LEVEL_NAMES, customLevelNames)
|
||||
return function (level) {
|
||||
let levelNum = 'default'
|
||||
if (Number.isInteger(+level)) {
|
||||
levelNum = Object.prototype.hasOwnProperty.call(levels, level) ? level : levelNum
|
||||
} else {
|
||||
levelNum = Object.prototype.hasOwnProperty.call(levelNames, level.toLowerCase()) ? levelNames[level.toLowerCase()] : levelNum
|
||||
}
|
||||
|
||||
return [levels[levelNum], levelNum]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
'use strict';
|
||||
|
||||
const Utf8Stream = require('./utils/Utf8Stream');
|
||||
|
||||
const patterns = {
|
||||
value1: /^(?:[\"\{\[\]\-\d]|true\b|false\b|null\b|\s{1,256})/,
|
||||
string: /^(?:[^\x00-\x1f\"\\]{1,256}|\\[bfnrt\"\\\/]|\\u[\da-fA-F]{4}|\")/,
|
||||
key1: /^(?:[\"\}]|\s{1,256})/,
|
||||
colon: /^(?:\:|\s{1,256})/,
|
||||
comma: /^(?:[\,\]\}]|\s{1,256})/,
|
||||
ws: /^\s{1,256}/,
|
||||
numberStart: /^\d/,
|
||||
numberDigit: /^\d{0,256}/,
|
||||
numberFraction: /^[\.eE]/,
|
||||
numberExponent: /^[eE]/,
|
||||
numberExpSign: /^[-+]/
|
||||
};
|
||||
const MAX_PATTERN_SIZE = 16;
|
||||
|
||||
let noSticky = true;
|
||||
try {
|
||||
new RegExp('.', 'y');
|
||||
noSticky = false;
|
||||
} catch (e) {
|
||||
// suppress
|
||||
}
|
||||
|
||||
!noSticky &&
|
||||
Object.keys(patterns).forEach(key => {
|
||||
let src = patterns[key].source.slice(1); // lop off ^
|
||||
if (src.slice(0, 3) === '(?:' && src.slice(-1) === ')') {
|
||||
src = src.slice(3, -1);
|
||||
}
|
||||
patterns[key] = new RegExp(src, 'y');
|
||||
});
|
||||
|
||||
patterns.numberFracStart = patterns.numberExpStart = patterns.numberStart;
|
||||
patterns.numberFracDigit = patterns.numberExpDigit = patterns.numberDigit;
|
||||
|
||||
const values = {true: true, false: false, null: null},
|
||||
expected = {object: 'objectStop', array: 'arrayStop', '': 'done'};
|
||||
|
||||
// long hexadecimal codes: \uXXXX
|
||||
const fromHex = s => String.fromCharCode(parseInt(s.slice(2), 16));
|
||||
|
||||
// short codes: \b \f \n \r \t \" \\ \/
|
||||
const codes = {b: '\b', f: '\f', n: '\n', r: '\r', t: '\t', '"': '"', '\\': '\\', '/': '/'};
|
||||
|
||||
class Parser extends Utf8Stream {
|
||||
static make(options) {
|
||||
return new Parser(options);
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
super(Object.assign({}, options, {readableObjectMode: true}));
|
||||
|
||||
this._packKeys = this._packStrings = this._packNumbers = this._streamKeys = this._streamStrings = this._streamNumbers = true;
|
||||
if (options) {
|
||||
'packValues' in options && (this._packKeys = this._packStrings = this._packNumbers = options.packValues);
|
||||
'packKeys' in options && (this._packKeys = options.packKeys);
|
||||
'packStrings' in options && (this._packStrings = options.packStrings);
|
||||
'packNumbers' in options && (this._packNumbers = options.packNumbers);
|
||||
'streamValues' in options && (this._streamKeys = this._streamStrings = this._streamNumbers = options.streamValues);
|
||||
'streamKeys' in options && (this._streamKeys = options.streamKeys);
|
||||
'streamStrings' in options && (this._streamStrings = options.streamStrings);
|
||||
'streamNumbers' in options && (this._streamNumbers = options.streamNumbers);
|
||||
this._jsonStreaming = options.jsonStreaming;
|
||||
}
|
||||
!this._packKeys && (this._streamKeys = true);
|
||||
!this._packStrings && (this._streamStrings = true);
|
||||
!this._packNumbers && (this._streamNumbers = true);
|
||||
|
||||
this._done = false;
|
||||
this._expect = this._jsonStreaming ? 'done' : 'value';
|
||||
this._stack = [];
|
||||
this._parent = '';
|
||||
this._open_number = false;
|
||||
this._accumulator = '';
|
||||
}
|
||||
|
||||
_flush(callback) {
|
||||
this._done = true;
|
||||
super._flush(error => {
|
||||
if (error) return callback(error);
|
||||
if (this._open_number) {
|
||||
if (this._streamNumbers) {
|
||||
this.push({name: 'endNumber'});
|
||||
}
|
||||
this._open_number = false;
|
||||
if (this._packNumbers) {
|
||||
this.push({name: 'numberValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
_processBuffer(callback) {
|
||||
let match,
|
||||
value,
|
||||
index = 0;
|
||||
main: for (;;) {
|
||||
switch (this._expect) {
|
||||
case 'value1':
|
||||
case 'value':
|
||||
patterns.value1.lastIndex = index;
|
||||
match = patterns.value1.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (this._done || index + MAX_PATTERN_SIZE < this._buffer.length) {
|
||||
if (index < this._buffer.length) return callback(new Error('Parser cannot parse input: expected a value'));
|
||||
return callback(new Error('Parser has expected a value'));
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
switch (value) {
|
||||
case '"':
|
||||
this._streamStrings && this.push({name: 'startString'});
|
||||
this._expect = 'string';
|
||||
break;
|
||||
case '{':
|
||||
this.push({name: 'startObject'});
|
||||
this._stack.push(this._parent);
|
||||
this._parent = 'object';
|
||||
this._expect = 'key1';
|
||||
break;
|
||||
case '[':
|
||||
this.push({name: 'startArray'});
|
||||
this._stack.push(this._parent);
|
||||
this._parent = 'array';
|
||||
this._expect = 'value1';
|
||||
break;
|
||||
case ']':
|
||||
if (this._expect !== 'value1') return callback(new Error("Parser cannot parse input: unexpected token ']'"));
|
||||
if (this._open_number) {
|
||||
this._streamNumbers && this.push({name: 'endNumber'});
|
||||
this._open_number = false;
|
||||
if (this._packNumbers) {
|
||||
this.push({name: 'numberValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
}
|
||||
this.push({name: 'endArray'});
|
||||
this._parent = this._stack.pop();
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
case '-':
|
||||
this._open_number = true;
|
||||
if (this._streamNumbers) {
|
||||
this.push({name: 'startNumber'});
|
||||
this.push({name: 'numberChunk', value: '-'});
|
||||
}
|
||||
this._packNumbers && (this._accumulator = '-');
|
||||
this._expect = 'numberStart';
|
||||
break;
|
||||
case '0':
|
||||
this._open_number = true;
|
||||
if (this._streamNumbers) {
|
||||
this.push({name: 'startNumber'});
|
||||
this.push({name: 'numberChunk', value: '0'});
|
||||
}
|
||||
this._packNumbers && (this._accumulator = '0');
|
||||
this._expect = 'numberFraction';
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
this._open_number = true;
|
||||
if (this._streamNumbers) {
|
||||
this.push({name: 'startNumber'});
|
||||
this.push({name: 'numberChunk', value: value});
|
||||
}
|
||||
this._packNumbers && (this._accumulator = value);
|
||||
this._expect = 'numberDigit';
|
||||
break;
|
||||
case 'true':
|
||||
case 'false':
|
||||
case 'null':
|
||||
if (this._buffer.length - index === value.length && !this._done) break main; // wait for more input
|
||||
this.push({name: value + 'Value', value: values[value]});
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
// default: // ws
|
||||
}
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'keyVal':
|
||||
case 'string':
|
||||
patterns.string.lastIndex = index;
|
||||
match = patterns.string.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length && (this._done || this._buffer.length - index >= 6))
|
||||
return callback(new Error('Parser cannot parse input: escaped characters'));
|
||||
if (this._done) return callback(new Error('Parser has expected a string value'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
if (value === '"') {
|
||||
if (this._expect === 'keyVal') {
|
||||
this._streamKeys && this.push({name: 'endKey'});
|
||||
if (this._packKeys) {
|
||||
this.push({name: 'keyValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
this._expect = 'colon';
|
||||
} else {
|
||||
this._streamStrings && this.push({name: 'endString'});
|
||||
if (this._packStrings) {
|
||||
this.push({name: 'stringValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
this._expect = expected[this._parent];
|
||||
}
|
||||
} else if (value.length > 1 && value.charAt(0) === '\\') {
|
||||
const t = value.length == 2 ? codes[value.charAt(1)] : fromHex(value);
|
||||
if (this._expect === 'keyVal' ? this._streamKeys : this._streamStrings) {
|
||||
this.push({name: 'stringChunk', value: t});
|
||||
}
|
||||
if (this._expect === 'keyVal' ? this._packKeys : this._packStrings) {
|
||||
this._accumulator += t;
|
||||
}
|
||||
} else {
|
||||
if (this._expect === 'keyVal' ? this._streamKeys : this._streamStrings) {
|
||||
this.push({name: 'stringChunk', value: value});
|
||||
}
|
||||
if (this._expect === 'keyVal' ? this._packKeys : this._packStrings) {
|
||||
this._accumulator += value;
|
||||
}
|
||||
}
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'key1':
|
||||
case 'key':
|
||||
patterns.key1.lastIndex = index;
|
||||
match = patterns.key1.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected an object key'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
if (value === '"') {
|
||||
this._streamKeys && this.push({name: 'startKey'});
|
||||
this._expect = 'keyVal';
|
||||
} else if (value === '}') {
|
||||
if (this._expect !== 'key1') return callback(new Error("Parser cannot parse input: unexpected token '}'"));
|
||||
this.push({name: 'endObject'});
|
||||
this._parent = this._stack.pop();
|
||||
this._expect = expected[this._parent];
|
||||
}
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'colon':
|
||||
patterns.colon.lastIndex = index;
|
||||
match = patterns.colon.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error("Parser cannot parse input: expected ':'"));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
value === ':' && (this._expect = 'value');
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'arrayStop':
|
||||
case 'objectStop':
|
||||
patterns.comma.lastIndex = index;
|
||||
match = patterns.comma.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error("Parser cannot parse input: expected ','"));
|
||||
break main; // wait for more input
|
||||
}
|
||||
if (this._open_number) {
|
||||
this._streamNumbers && this.push({name: 'endNumber'});
|
||||
this._open_number = false;
|
||||
if (this._packNumbers) {
|
||||
this.push({name: 'numberValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
}
|
||||
value = match[0];
|
||||
if (value === ',') {
|
||||
this._expect = this._expect === 'arrayStop' ? 'value' : 'key';
|
||||
} else if (value === '}' || value === ']') {
|
||||
if (value === '}' ? this._expect === 'arrayStop' : this._expect !== 'arrayStop') {
|
||||
return callback(new Error("Parser cannot parse input: expected '" + (this._expect === 'arrayStop' ? ']' : '}') + "'"));
|
||||
}
|
||||
this.push({name: value === '}' ? 'endObject' : 'endArray'});
|
||||
this._parent = this._stack.pop();
|
||||
this._expect = expected[this._parent];
|
||||
}
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
// number chunks
|
||||
case 'numberStart': // [0-9]
|
||||
patterns.numberStart.lastIndex = index;
|
||||
match = patterns.numberStart.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a starting digit'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = value === '0' ? 'numberFraction' : 'numberDigit';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberDigit': // [0-9]*
|
||||
patterns.numberDigit.lastIndex = index;
|
||||
match = patterns.numberDigit.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a digit'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
if (value) {
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
} else {
|
||||
if (index < this._buffer.length) {
|
||||
this._expect = 'numberFraction';
|
||||
break;
|
||||
}
|
||||
if (this._done) {
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
break;
|
||||
case 'numberFraction': // [\.eE]?
|
||||
patterns.numberFraction.lastIndex = index;
|
||||
match = patterns.numberFraction.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) {
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = value === '.' ? 'numberFracStart' : 'numberExpSign';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberFracStart': // [0-9]
|
||||
patterns.numberFracStart.lastIndex = index;
|
||||
match = patterns.numberFracStart.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected a fractional part of a number'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = 'numberFracDigit';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberFracDigit': // [0-9]*
|
||||
patterns.numberFracDigit.lastIndex = index;
|
||||
match = patterns.numberFracDigit.exec(this._buffer);
|
||||
value = match[0];
|
||||
if (value) {
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
} else {
|
||||
if (index < this._buffer.length) {
|
||||
this._expect = 'numberExponent';
|
||||
break;
|
||||
}
|
||||
if (this._done) {
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
break;
|
||||
case 'numberExponent': // [eE]?
|
||||
patterns.numberExponent.lastIndex = index;
|
||||
match = patterns.numberExponent.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length) {
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
}
|
||||
if (this._done) {
|
||||
this._expect = 'done';
|
||||
break;
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = 'numberExpSign';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberExpSign': // [-+]?
|
||||
patterns.numberExpSign.lastIndex = index;
|
||||
match = patterns.numberExpSign.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length) {
|
||||
this._expect = 'numberExpStart';
|
||||
break;
|
||||
}
|
||||
if (this._done) return callback(new Error('Parser has expected an exponent value of a number'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = 'numberExpStart';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberExpStart': // [0-9]
|
||||
patterns.numberExpStart.lastIndex = index;
|
||||
match = patterns.numberExpStart.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length || this._done) return callback(new Error('Parser cannot parse input: expected an exponent part of a number'));
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
this._expect = 'numberExpDigit';
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
case 'numberExpDigit': // [0-9]*
|
||||
patterns.numberExpDigit.lastIndex = index;
|
||||
match = patterns.numberExpDigit.exec(this._buffer);
|
||||
value = match[0];
|
||||
if (value) {
|
||||
this._streamNumbers && this.push({name: 'numberChunk', value: value});
|
||||
this._packNumbers && (this._accumulator += value);
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
} else {
|
||||
if (index < this._buffer.length || this._done) {
|
||||
this._expect = expected[this._parent];
|
||||
break;
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
break;
|
||||
case 'done':
|
||||
patterns.ws.lastIndex = index;
|
||||
match = patterns.ws.exec(this._buffer);
|
||||
if (!match) {
|
||||
if (index < this._buffer.length) {
|
||||
if (this._jsonStreaming) {
|
||||
this._expect = 'value';
|
||||
break;
|
||||
}
|
||||
return callback(new Error('Parser cannot parse input: unexpected characters'));
|
||||
}
|
||||
break main; // wait for more input
|
||||
}
|
||||
value = match[0];
|
||||
if (this._open_number) {
|
||||
this._streamNumbers && this.push({name: 'endNumber'});
|
||||
this._open_number = false;
|
||||
if (this._packNumbers) {
|
||||
this.push({name: 'numberValue', value: this._accumulator});
|
||||
this._accumulator = '';
|
||||
}
|
||||
}
|
||||
if (noSticky) {
|
||||
this._buffer = this._buffer.slice(value.length);
|
||||
} else {
|
||||
index += value.length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
!noSticky && (this._buffer = this._buffer.slice(index));
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
Parser.parser = Parser.make;
|
||||
Parser.make.Constructor = Parser;
|
||||
|
||||
module.exports = Parser;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './compilerOptions';
|
||||
export * from './getParsedConfigFile';
|
||||
@@ -0,0 +1,22 @@
|
||||
declare module 'buffer' {
|
||||
export const INSPECT_MAX_BYTES: number;
|
||||
export const kMaxLength: number;
|
||||
export const kStringMaxLength: number;
|
||||
export const constants: {
|
||||
MAX_LENGTH: number;
|
||||
MAX_STRING_LENGTH: number;
|
||||
};
|
||||
const BuffType: typeof Buffer;
|
||||
|
||||
export type TranscodeEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "latin1" | "binary";
|
||||
|
||||
export function transcode(source: Uint8Array, fromEnc: TranscodeEncoding, toEnc: TranscodeEncoding): Buffer;
|
||||
|
||||
export const SlowBuffer: {
|
||||
/** @deprecated since v6.0.0, use Buffer.allocUnsafeSlow() */
|
||||
new(size: number): Buffer;
|
||||
prototype: Buffer;
|
||||
};
|
||||
|
||||
export { BuffType as Buffer };
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag the use of redundant constructors in classes.
|
||||
* @author Alberto Rodríguez
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether any of a method's parameters have a decorator or are a parameter property.
|
||||
* @param {ASTNode} node A method definition node.
|
||||
* @returns {boolean} `true` if any parameter had a decorator or is a parameter property.
|
||||
*/
|
||||
function hasDecoratorsOrParameterProperty(node) {
|
||||
return node.value.params.some(
|
||||
param =>
|
||||
param.decorators?.length || param.type === "TSParameterProperty",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node's accessibility makes it not useless.
|
||||
* @param {ASTNode} node A method definition node.
|
||||
* @returns {boolean} `true` if the node has a useful accessibility.
|
||||
*/
|
||||
function hasUsefulAccessibility(node) {
|
||||
switch (node.accessibility) {
|
||||
case "protected":
|
||||
case "private":
|
||||
return true;
|
||||
case "public":
|
||||
return !!node.parent.parent.superClass;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given array of statements is a single call of `super`.
|
||||
* @param {ASTNode[]} body An array of statements to check.
|
||||
* @returns {boolean} `true` if the body is a single call of `super`.
|
||||
*/
|
||||
function isSingleSuperCall(body) {
|
||||
return (
|
||||
body.length === 1 &&
|
||||
body[0].type === "ExpressionStatement" &&
|
||||
body[0].expression.type === "CallExpression" &&
|
||||
body[0].expression.callee.type === "Super"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given node is a pattern which doesn't have any side effects.
|
||||
* Default parameters and Destructuring parameters can have side effects.
|
||||
* @param {ASTNode} node A pattern node.
|
||||
* @returns {boolean} `true` if the node doesn't have any side effects.
|
||||
*/
|
||||
function isSimple(node) {
|
||||
return node.type === "Identifier" || node.type === "RestElement";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given array of expressions is `...arguments` or not.
|
||||
* `super(...arguments)` passes all arguments through.
|
||||
* @param {ASTNode[]} superArgs An array of expressions to check.
|
||||
* @returns {boolean} `true` if the superArgs is `...arguments`.
|
||||
*/
|
||||
function isSpreadArguments(superArgs) {
|
||||
return (
|
||||
superArgs.length === 1 &&
|
||||
superArgs[0].type === "SpreadElement" &&
|
||||
superArgs[0].argument.type === "Identifier" &&
|
||||
superArgs[0].argument.name === "arguments"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given 2 nodes are identifiers which have the same name or not.
|
||||
* @param {ASTNode} ctorParam A node to check.
|
||||
* @param {ASTNode} superArg A node to check.
|
||||
* @returns {boolean} `true` if the nodes are identifiers which have the same
|
||||
* name.
|
||||
*/
|
||||
function isValidIdentifierPair(ctorParam, superArg) {
|
||||
return (
|
||||
ctorParam.type === "Identifier" &&
|
||||
superArg.type === "Identifier" &&
|
||||
ctorParam.name === superArg.name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given 2 nodes are a rest/spread pair which has the same values.
|
||||
* @param {ASTNode} ctorParam A node to check.
|
||||
* @param {ASTNode} superArg A node to check.
|
||||
* @returns {boolean} `true` if the nodes are a rest/spread pair which has the
|
||||
* same values.
|
||||
*/
|
||||
function isValidRestSpreadPair(ctorParam, superArg) {
|
||||
return (
|
||||
ctorParam.type === "RestElement" &&
|
||||
superArg.type === "SpreadElement" &&
|
||||
isValidIdentifierPair(ctorParam.argument, superArg.argument)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given 2 nodes have the same value or not.
|
||||
* @param {ASTNode} ctorParam A node to check.
|
||||
* @param {ASTNode} superArg A node to check.
|
||||
* @returns {boolean} `true` if the nodes have the same value or not.
|
||||
*/
|
||||
function isValidPair(ctorParam, superArg) {
|
||||
return (
|
||||
isValidIdentifierPair(ctorParam, superArg) ||
|
||||
isValidRestSpreadPair(ctorParam, superArg)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameters of a constructor and the arguments of `super()`
|
||||
* have the same values or not.
|
||||
* @param {ASTNode} ctorParams The parameters of a constructor to check.
|
||||
* @param {ASTNode} superArgs The arguments of `super()` to check.
|
||||
* @returns {boolean} `true` if those have the same values.
|
||||
*/
|
||||
function isPassingThrough(ctorParams, superArgs) {
|
||||
if (ctorParams.length !== superArgs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < ctorParams.length; ++i) {
|
||||
if (!isValidPair(ctorParams[i], superArgs[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the constructor body is a redundant super call.
|
||||
* @param {Array} body constructor body content.
|
||||
* @param {Array} ctorParams The params to check against super call.
|
||||
* @returns {boolean} true if the constructor body is redundant
|
||||
*/
|
||||
function isRedundantSuperCall(body, ctorParams) {
|
||||
return (
|
||||
isSingleSuperCall(body) &&
|
||||
ctorParams.every(isSimple) &&
|
||||
(isSpreadArguments(body[0].expression.arguments) ||
|
||||
isPassingThrough(ctorParams, body[0].expression.arguments))
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow unnecessary constructors",
|
||||
dialects: ["JavaScript", "TypeScript"],
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/no-useless-constructor",
|
||||
},
|
||||
|
||||
hasSuggestions: true,
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
noUselessConstructor: "Useless constructor.",
|
||||
removeConstructor: "Remove the constructor.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const { sourceCode } = context;
|
||||
|
||||
/**
|
||||
* Checks whether a node is a redundant constructor
|
||||
* @param {ASTNode} node node to check
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForConstructor(node) {
|
||||
if (
|
||||
node.kind !== "constructor" ||
|
||||
node.value.type !== "FunctionExpression" ||
|
||||
hasDecoratorsOrParameterProperty(node) ||
|
||||
hasUsefulAccessibility(node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent crashing on parsers which do not require class constructor
|
||||
* to have a body, e.g. typescript and flow
|
||||
*/
|
||||
if (!node.value.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const body = node.value.body.body;
|
||||
const ctorParams = node.value.params;
|
||||
const superClass = node.parent.parent.superClass;
|
||||
const parenToken = sourceCode.getFirstToken(
|
||||
node,
|
||||
astUtils.isOpeningParenToken,
|
||||
);
|
||||
const loc = {
|
||||
start: node.loc.start,
|
||||
end: sourceCode.getTokenBefore(parenToken).loc.end,
|
||||
};
|
||||
|
||||
if (
|
||||
superClass
|
||||
? isRedundantSuperCall(body, ctorParams)
|
||||
: body.length === 0
|
||||
) {
|
||||
context.report({
|
||||
loc,
|
||||
messageId: "noUselessConstructor",
|
||||
suggest: [
|
||||
{
|
||||
messageId: "removeConstructor",
|
||||
*fix(fixer) {
|
||||
const nextToken =
|
||||
sourceCode.getTokenAfter(node);
|
||||
const addSemiColon =
|
||||
astUtils.canContinueExpressionInClassBody(
|
||||
nextToken,
|
||||
) &&
|
||||
astUtils.needsPrecedingSemicolon(
|
||||
sourceCode,
|
||||
node,
|
||||
);
|
||||
|
||||
yield fixer.replaceText(
|
||||
node,
|
||||
addSemiColon ? ";" : "",
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
MethodDefinition: checkForConstructor,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const util_1 = require("../util");
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'no-empty-interface',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
deprecated: {
|
||||
deprecatedSince: '8.0.0',
|
||||
replacedBy: [
|
||||
{
|
||||
rule: {
|
||||
name: '@typescript-eslint/no-empty-object-type',
|
||||
url: 'https://typescript-eslint.io/rules/no-empty-object-type',
|
||||
},
|
||||
},
|
||||
],
|
||||
url: 'https://github.com/typescript-eslint/typescript-eslint/pull/8977',
|
||||
},
|
||||
docs: {
|
||||
description: 'Disallow the declaration of empty interfaces',
|
||||
},
|
||||
fixable: 'code',
|
||||
hasSuggestions: true,
|
||||
messages: {
|
||||
noEmpty: 'An empty interface is equivalent to `{}`.',
|
||||
noEmptyWithSuper: 'An interface declaring no members is equivalent to its supertype.',
|
||||
},
|
||||
replacedBy: ['@typescript-eslint/no-empty-object-type'],
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowSingleExtends: {
|
||||
type: 'boolean',
|
||||
description: 'Whether to allow empty interfaces that extend a single other interface.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: [
|
||||
{
|
||||
allowSingleExtends: false,
|
||||
},
|
||||
],
|
||||
create(context, [{ allowSingleExtends }]) {
|
||||
return {
|
||||
TSInterfaceDeclaration(node) {
|
||||
if (node.body.body.length !== 0) {
|
||||
// interface contains members --> Nothing to report
|
||||
return;
|
||||
}
|
||||
const extend = node.extends;
|
||||
if (extend.length === 0) {
|
||||
context.report({
|
||||
node: node.id,
|
||||
messageId: 'noEmpty',
|
||||
});
|
||||
}
|
||||
else if (extend.length === 1 &&
|
||||
// interface extends exactly 1 interface --> Report depending on rule setting
|
||||
!allowSingleExtends) {
|
||||
const fix = (fixer) => {
|
||||
let typeParam = '';
|
||||
if (node.typeParameters) {
|
||||
typeParam = context.sourceCode.getText(node.typeParameters);
|
||||
}
|
||||
return fixer.replaceText(node, `type ${context.sourceCode.getText(node.id)}${typeParam} = ${context.sourceCode.getText(extend[0])}`);
|
||||
};
|
||||
const scope = context.sourceCode.getScope(node);
|
||||
const mergedWithClassDeclaration = scope.set
|
||||
.get(node.id.name)
|
||||
?.defs.some(def => def.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration);
|
||||
const isInAmbientDeclaration = (0, util_1.isDefinitionFile)(context.filename) &&
|
||||
scope.type === scope_manager_1.ScopeType.tsModule &&
|
||||
scope.block.declare;
|
||||
const useAutoFix = !(isInAmbientDeclaration || mergedWithClassDeclaration);
|
||||
context.report({
|
||||
node: node.id,
|
||||
messageId: 'noEmptyWithSuper',
|
||||
...(useAutoFix
|
||||
? { fix }
|
||||
: !mergedWithClassDeclaration
|
||||
? {
|
||||
suggest: [
|
||||
{
|
||||
messageId: 'noEmptyWithSuper',
|
||||
fix,
|
||||
},
|
||||
],
|
||||
}
|
||||
: null),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
function _toSetter(t, e, n) {
|
||||
e || (e = []);
|
||||
var r = e.length++;
|
||||
return Object.defineProperty({}, "_", {
|
||||
set: function set(o) {
|
||||
e[r] = o, t.apply(n, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
module.exports = _toSetter, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
Reference in New Issue
Block a user