@terrazzo/parser 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Drew Powers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,104 @@
1
+ import type { DocumentNode } from '@humanwhocodes/momoa';
2
+ import type { ConfigInit } from '../config.js';
3
+ import type Logger from '../logger.js';
4
+ import type { TokenNormalized } from '../types.js';
5
+
6
+ export interface BuildRunnerOptions {
7
+ ast: DocumentNode;
8
+ config: ConfigInit;
9
+ logger?: Logger;
10
+ }
11
+
12
+ export interface OutputFile {
13
+ filename: string;
14
+ contents: string | Buffer;
15
+ }
16
+
17
+ /** Transformed token with a single value. Note that this may be any type! */
18
+ export interface TokenTransformedSingleValue {
19
+ /** ID unique to this format. If missing, use `token.id`. */
20
+ localID?: string;
21
+ type: 'SINGLE_VALUE';
22
+ value: string;
23
+ /** The mode of this value (default: `"."`) */
24
+ mode: string;
25
+ /** The original token */
26
+ token: TokenNormalized;
27
+ }
28
+
29
+ /** Transformed token with multiple values. Note that this may be any type! */
30
+ export interface TokenTransformedMultiValue {
31
+ /** ID unique to this format. If missing, use `token.id` */
32
+ localID?: string;
33
+ type: 'MULTI_VALUE';
34
+ value: Record<string, string>;
35
+ /** The mode of this value (default: `"."`) */
36
+ mode: string;
37
+ /** The original token */
38
+ token: TokenNormalized;
39
+ }
40
+
41
+ export type TokenTransformed = TokenTransformedSingleValue | TokenTransformedMultiValue;
42
+
43
+ export interface TransformParams {
44
+ /** ID of an existing format */
45
+ format: string;
46
+ /** Glob of tokens to select (e.g. `"color.*"` to select all tokens starting with `"color."`) */
47
+ select?: string | string[];
48
+ /** Mode name, if selecting a mode (default: `"."`) */
49
+ mode?: string | string[];
50
+ }
51
+
52
+ export interface TransformHookOptions {
53
+ /** Map of tokens */
54
+ tokens: Record<string, TokenNormalized>;
55
+ /** Query transformed values */
56
+ getTransforms(params: TransformParams): TokenTransformed;
57
+ /** Update transformed values */
58
+ setTransform(
59
+ id: string,
60
+ params: {
61
+ format: string;
62
+ localID?: string;
63
+ value: string | Record<string, string>;
64
+ mode?: string;
65
+ },
66
+ ): void;
67
+ /** Momoa document */
68
+ ast: DocumentNode;
69
+ }
70
+
71
+ export interface BuildHookOptions {
72
+ /** Map of tokens */
73
+ tokens: Record<string, TokenNormalized>;
74
+ /** Query transformed values */
75
+ getTransforms(params: TransformParams): TokenTransformed[];
76
+ /** Momoa document */
77
+ ast: DocumentNode;
78
+ outputFile: (
79
+ /** Filename to output (relative to outDir) */
80
+ filename: string,
81
+ /** Contents to write to file */
82
+ contents: string | Buffer,
83
+ ) => void;
84
+ }
85
+
86
+ export interface BuildRunnerResult {
87
+ outputFiles: OutputFile[];
88
+ }
89
+
90
+ export interface BuildEndHookOptions {
91
+ /** Map of tokens */
92
+ tokens: Record<string, TokenNormalized>;
93
+ /** Query transformed values */
94
+ getTransforms(params: TransformParams): TokenTransformed[];
95
+ /** Momoa document */
96
+ ast: DocumentNode;
97
+ /** Final files to be written */
98
+ outputFiles: OutputFile[];
99
+ }
100
+
101
+ export default function build(
102
+ tokens: Record<string, TokenNormalized>,
103
+ options: BuildRunnerOptions,
104
+ ): Promise<BuildRunnerResult>;
package/build/index.js ADDED
@@ -0,0 +1,182 @@
1
+ import { isTokenMatch } from '@terrazzo/token-tools';
2
+ import wcmatch from 'wildcard-match';
3
+ import Logger from '../logger.js';
4
+
5
+ /**
6
+ * @typedef {object} BuildRunnerOptions
7
+ * @typedef {Record<string, TokenNormalized>} BuildRunnerOptions.tokens
8
+ * @typedef {DocumentNode} BuildRunnerOptions.ast
9
+ * @typedef {ConfigInit} BuildRunnerOptions.config
10
+ * @typedef {Logger} BuildRunnerOptions.logger
11
+ * @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode
12
+ * @typedef {import("../config.js").ConfigInit} ConfigInit
13
+ * @typedef {import("../logger.js")} Logger
14
+ * @typedef {import("../types.js").TokenNormalized} TokenNormalized
15
+ *
16
+ * @typedef {object} BuildRunnerResult
17
+ * @typedef {OutputFile[]} BuildRunnerResult.outputFiles
18
+ * @typedef {object} OutputFile
19
+ * @typedef {string} OutputFile.filename
20
+ * @typedef {string | Buffer} OutputFile.contents
21
+ */
22
+
23
+ export const SINGLE_VALUE = 'SINGLE_VALUE';
24
+ export const MULTI_VALUE = 'MULTI_VALUE';
25
+
26
+ /** Validate plugin setTransform() calls for immediate feedback */
27
+ function validateTransformParams({ params, token, logger, pluginName }) {
28
+ const baseEntry = { group: 'plugin', task: pluginName };
29
+
30
+ // validate ID
31
+ if (!token) {
32
+ logger.error({
33
+ ...baseEntry,
34
+ message: `setTransform() tried to transform token "${id}" but it doesn’t exist.`,
35
+ });
36
+ }
37
+ // validate value is valid for SINGLE_VALUE or MULTI_VALUE
38
+ if (
39
+ !params.value ||
40
+ (typeof params.value !== 'string' && typeof params.value !== 'object') ||
41
+ Array.isArray(params.value)
42
+ ) {
43
+ logger.error({
44
+ ...baseEntry,
45
+ message: `setTransform() value expected string or object of strings, received ${
46
+ Array.isArray(params.value) ? 'Array' : typeof params.value
47
+ }`,
48
+ });
49
+ }
50
+ if (typeof params.value === 'object' && Object.values(params.value).some((v) => !v || typeof v !== 'string')) {
51
+ logger.error({
52
+ ...baseEntry,
53
+ message: 'setTransform() value expected object of strings, received some non-string values',
54
+ });
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Run build stage
60
+ * @param {BuildOptions} options
61
+ * @return {Promise<BuildResult>}
62
+ */
63
+ export default async function build(tokens, { ast, logger = new Logger(), config }) {
64
+ const formats = {};
65
+ const result = { outputFiles: [] };
66
+
67
+ function getTransforms(params) {
68
+ return (formats[params.format] ?? []).filter((token) => {
69
+ if (
70
+ params.select &&
71
+ params.select !== '*' &&
72
+ !isTokenMatch(token.token.id, Array.isArray(params.select) ? params.select : [params.select])
73
+ ) {
74
+ return false;
75
+ }
76
+ return !params.mode || wcmatch(params.mode)(token.mode);
77
+ });
78
+ }
79
+
80
+ // transform()
81
+ let transformsLocked = false; // prevent plugins from transforming after stage has ended
82
+ const startTransform = performance.now();
83
+ logger.debug({ group: 'parser', task: 'transform', message: 'Start transform' });
84
+ for (const plugin of config.plugins) {
85
+ if (typeof plugin.transform === 'function') {
86
+ await plugin.transform({
87
+ tokens,
88
+ ast,
89
+ getTransforms,
90
+ setTransform(id, params) {
91
+ if (transformsLocked) {
92
+ logger.warn({
93
+ message: 'Attempted to call setTransform() after transform step has completed.',
94
+ group: 'plugin',
95
+ task: plugin.name,
96
+ });
97
+ return;
98
+ }
99
+ const token = tokens[id];
100
+ validateTransformParams({ token, logger, params, pluginName: plugin.name });
101
+
102
+ // upsert
103
+ if (!formats[params.format]) {
104
+ formats[params.format] = [];
105
+ }
106
+ const foundTokenI = formats[params.format].findIndex(
107
+ (t) =>
108
+ params.localID === t.localID &&
109
+ (!params.mode || params.mode === t.mode) &&
110
+ (!params.variant || params.variant === t.variant),
111
+ );
112
+ if (foundTokenI === -1) {
113
+ formats[params.format].push({
114
+ ...params,
115
+ type: typeof params.value === 'string' ? SINGLE_VALUE : MULTI_VALUE,
116
+ mode: params.mode || '.',
117
+ token: structuredClone(token),
118
+ });
119
+ } else {
120
+ formats[params.format][foundTokenI].value = params.value;
121
+ formats[params.format][foundTokenI].type = typeof params.value === 'string' ? SINGLE_VALUE : MULTI_VALUE;
122
+ }
123
+ },
124
+ });
125
+ }
126
+ }
127
+ transformsLocked = true;
128
+ logger.debug({
129
+ group: 'parser',
130
+ task: 'transform',
131
+ message: 'Finish transform',
132
+ timing: performance.now() - startTransform,
133
+ });
134
+
135
+ // build()
136
+ const startBuild = performance.now();
137
+ logger.debug({ group: 'parser', task: 'build', message: 'Start build' });
138
+ for (const plugin of config.plugins) {
139
+ if (typeof plugin.build === 'function') {
140
+ await plugin.build({
141
+ tokens,
142
+ ast,
143
+ getTransforms,
144
+ outputFile(filename, contents) {
145
+ const resolved = new URL(filename, config.outDir);
146
+ if (result.outputFiles.some((f) => new URL(f.filename, config.outDir).href === resolved.href)) {
147
+ logger.error({ message: `Can’t overwrite file "${filename}"`, label: plugin.name });
148
+ }
149
+ result.outputFiles.push({ filename, contents });
150
+ },
151
+ });
152
+ }
153
+ }
154
+ logger.debug({
155
+ group: 'parser',
156
+ task: 'build',
157
+ message: 'Finish build',
158
+ timing: performance.now() - startBuild,
159
+ });
160
+
161
+ // buildEnd()
162
+ const startBuildEnd = performance.now();
163
+ logger.debug({ group: 'parser', task: 'build', message: 'Start buildEnd' });
164
+ for (const plugin of config.plugins) {
165
+ if (typeof plugin.buildEnd === 'function') {
166
+ await plugin.buildEnd({
167
+ tokens,
168
+ ast,
169
+ format: (formatID) => createFormatter(formatID),
170
+ outputFiles: structruedClone(result.outputFiles),
171
+ });
172
+ }
173
+ }
174
+ logger.debug({
175
+ group: 'parser',
176
+ task: 'build',
177
+ message: 'Finish buildEnd',
178
+ timing: performance.now() - startBuildEnd,
179
+ });
180
+
181
+ return result;
182
+ }
package/config.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ import type { BuildHookOptions, BuildRunnerResult, TransformHookOptions } from './build/index.js';
2
+ import type { LintRuleShorthand, LintRuleLonghand, Linter } from './lint/index.js';
3
+ import type Logger from './logger.js';
4
+
5
+ export interface Config {
6
+ /** Path to tokens.json (default: "./tokens.json") */
7
+ tokens?: string | string[];
8
+ /** Output directory (default: "./tokens/") */
9
+ outDir?: string;
10
+ /** Specify plugins */
11
+ plugins?: Plugin[];
12
+ /** Specify linting settings */
13
+ lint?: {
14
+ /** Configure build behavior */
15
+ build?: {
16
+ /** Should linters run with `co build`? (default: true) */
17
+ enabled?: boolean;
18
+ };
19
+ /** Configure lint rules */
20
+ rules?: Record<string, LintRuleShorthand | LintRuleLonghand>;
21
+ };
22
+ }
23
+
24
+ export interface ConfigInit {
25
+ tokens: URL[];
26
+ outDir: URL;
27
+ plugins: Plugin[];
28
+ lint: {
29
+ build: NonNullable<NonNullable<Config['lint']>['build']>;
30
+ rules: Record<string, LintRuleLonghand>;
31
+ };
32
+ }
33
+
34
+ export interface Plugin {
35
+ name: string;
36
+ /** Read config, and optionally modify */
37
+ // biome-ignore lint/suspicious/noConfusingVoidType format: this helps plugins be a little looser on their typing
38
+ config?(config: ConfigInit): void | ConfigInit | undefined;
39
+ /**
40
+ * Declare:
41
+ * - `"pre"`: run this plugin BEFORE all others
42
+ * - `"post"`: run this plugin AFTER all others
43
+ * - (default) run this plugin in default order (array order)
44
+ */
45
+ enforce?: 'pre' | 'post';
46
+ /** Throw lint errors/warnings */
47
+ lint?(): Record<string, Linter>;
48
+ transform?(options: TransformHookOptions): Promise<void>;
49
+ build?(options: BuildHookOptions): Promise<void>;
50
+ buildEnd?(result: BuildRunnerResult): Promise<void>;
51
+ }
52
+
53
+ export interface ConfigOptions {
54
+ logger?: Logger;
55
+ /** @terrazzo/parser needs cwd so this can be run without Node.js. Importing defineConfig from @terrazzo/cli doesn’t need this. */
56
+ cwd: URL;
57
+ }
58
+
59
+ /**
60
+ * Validate and normalize a config
61
+ */
62
+ export default function defineConfig(rawConfig: Config, options: ConfigOptions): ConfigInit;
63
+
64
+ export function mergeConfigs(a: Config, b: Config): Config;
package/config.js ADDED
@@ -0,0 +1,196 @@
1
+ import { merge } from 'merge-anything';
2
+ import coreLintPlugin from './lint/plugin-core/index.js';
3
+ import Logger from './logger.js';
4
+
5
+ const TRAILING_SLASH_RE = /\/*$/;
6
+
7
+ /**
8
+ * Validate and normalize a config
9
+ * @param {Config} rawConfig
10
+ * @param {object} options
11
+ * @param {Logger} options.logger
12
+ * @param {URL} options.cwd
13
+ */
14
+ export default function defineConfig(rawConfig, { logger = new Logger(), cwd = import.meta.url } = {}) {
15
+ const configStart = performance.now();
16
+
17
+ logger.debug({ group: 'parser', task: 'config', message: 'Start config validation' });
18
+
19
+ const config = { ...rawConfig };
20
+
21
+ // config.tokens
22
+ if (rawConfig.tokens === undefined) {
23
+ config.tokens = ['./tokens.json']; // will be normalized in next step
24
+ } else if (typeof rawConfig.tokens === 'string') {
25
+ config.tokens = [rawConfig.tokens]; // will be normalized in next step
26
+ } else if (Array.isArray(rawConfig.tokens)) {
27
+ config.tokens = [];
28
+ for (const file of rawConfig.tokens) {
29
+ if (typeof file === 'string') {
30
+ config.tokens.push(file); // will be normalized in next step
31
+ } else {
32
+ logger.error({
33
+ label: 'config.tokens',
34
+ message: `Expected array of strings, encountered ${JSON.stringify(file)}`,
35
+ });
36
+ }
37
+ }
38
+ } else {
39
+ logger.error({
40
+ label: 'config.tokens',
41
+ message: `Expected string or array of strings, received ${typeof rawConfig.tokens}`,
42
+ });
43
+ }
44
+ for (let i = 0; i < config.tokens.length; i++) {
45
+ const filepath = config.tokens[i];
46
+ try {
47
+ config.tokens[i] = new URL(filepath, cwd);
48
+ } catch {
49
+ logger.error({ label: 'config.tokens', message: `Invalid URL ${filepath}` });
50
+ }
51
+ }
52
+
53
+ // config.outDir
54
+ if (typeof config.outDir === 'undefined') {
55
+ config.outDir = new URL('./tokens/', cwd);
56
+ } else if (typeof config.outDir !== 'string') {
57
+ logger.error({ label: 'config.outDir', message: `Expected string, received ${JSON.stringify(config.outDir)}` });
58
+ } else {
59
+ // note: always add trailing slash so URL treats it as a directory
60
+ config.outDir = new URL(config.outDir.replace(TRAILING_SLASH_RE, '/'), cwd);
61
+ }
62
+
63
+ // config.plugins
64
+ if (typeof config.plugins === 'undefined') {
65
+ config.plugins = [];
66
+ }
67
+ if (!Array.isArray(config.plugins)) {
68
+ logger.error({
69
+ label: 'config.plugins',
70
+ message: `Expected array of plugins, received ${JSON.stringify(config.plugins)}`,
71
+ });
72
+ }
73
+ config.plugins.push(coreLintPlugin());
74
+ for (let n = 0; n < config.plugins.length; n++) {
75
+ const plugin = config.plugins[n];
76
+ if (typeof plugin !== 'object') {
77
+ logger.error({ label: `plugin[${n}]`, message: `Expected output plugin, received ${JSON.stringify(plugin)}` });
78
+ } else if (!plugin.name) {
79
+ logger.error({ label: `plugin[${n}]`, message: `Missing "name"` });
80
+ }
81
+ }
82
+ // order plugins with "enforce"
83
+ config.plugins.sort((a, b) => {
84
+ if (a.enforce === 'pre' && b.enforce !== 'pre') {
85
+ return -1;
86
+ } else if (a.enforce === 'post' && b.enforce !== 'post') {
87
+ return 1;
88
+ }
89
+ return 0;
90
+ });
91
+
92
+ // config.lint
93
+ if (config.lint !== undefined) {
94
+ if (config.lint === null || typeof config.lint !== 'object' || Array.isArray(config.lint)) {
95
+ logger.error({ label: 'config.lint', message: 'Must be an object' });
96
+ return config;
97
+ }
98
+
99
+ if (!config.lint.build) {
100
+ config.lint.build = { enabled: true };
101
+ }
102
+ if (config.lint.build.enabled !== undefined) {
103
+ if (typeof config.lint.build.enabled !== 'boolean') {
104
+ logger.error({
105
+ label: 'config.lint.build.enabled',
106
+ message: `Expected boolean, received ${JSON.stringify(config.lint.build)}`,
107
+ });
108
+ }
109
+ } else {
110
+ config.lint.build.enabled = true;
111
+ }
112
+
113
+ if (config.lint.rules !== undefined) {
114
+ if (config.lint.rules === null || typeof config.lint.rules !== 'object' || Array.isArray(config.lint.rules)) {
115
+ logger.error({
116
+ label: 'config.lint.rules',
117
+ message: `Expected object, received ${JSON.stringify(config.lint.rules)}`,
118
+ });
119
+ }
120
+
121
+ for (const id in config.lint.rules) {
122
+ if (!Object.hasOwn(config.lint.rules, id)) {
123
+ continue;
124
+ }
125
+ if (typeof id !== 'string') {
126
+ logger.error({ label: 'config.lint.rules', message: `Expects string keys, received ${JSON.stringify(id)}` });
127
+ }
128
+ const value = config.lint.rules[id];
129
+ let severity = 'off';
130
+ let options;
131
+ if (typeof value === 'number' || typeof value === 'string') {
132
+ severity = value;
133
+ } else if (Array.isArray(value)) {
134
+ severity = value[0];
135
+ options = value[1];
136
+ } else if (value !== undefined) {
137
+ logger.error({
138
+ label: `config.lint.rule:${id}`,
139
+ message: `Invalid eyntax. Expected \`string | number | Array\`, received ${JSON.stringify(value)}}`,
140
+ });
141
+ }
142
+ config.lint.rules[id] = { id, severity, options };
143
+ if (typeof severity === 'number') {
144
+ if (severity !== 0 && severity !== 1 && severity !== 2) {
145
+ logger.error({
146
+ label: `config.lint.rule:${id}`,
147
+ message: `Invalid number ${severity}. Specify 0 (off), 1 (warn), or 2 (error).`,
148
+ });
149
+ return config;
150
+ }
151
+ config.lint.rules[id].severity = ['off', 'warn', 'error'][severity];
152
+ } else if (typeof severity === 'string') {
153
+ if (severity !== 'off' && severity !== 'warn' && severity !== 'error') {
154
+ logger.error({
155
+ label: `config.lint.rule:${id}`,
156
+ message: `Invalid string ${JSON.stringify(severity)}. Specify "off", "warn", or "error".`,
157
+ });
158
+ }
159
+ } else if (value !== null) {
160
+ logger.error({
161
+ label: `config.lint.rule:${id}`,
162
+ message: `Expected string or number, received ${JSON.stringify(value)}`,
163
+ });
164
+ }
165
+ }
166
+ }
167
+ } else {
168
+ config.lint = {
169
+ build: { enabled: true },
170
+ rules: {},
171
+ };
172
+ }
173
+
174
+ // call plugin.config()
175
+ for (const plugin of config.plugins) {
176
+ plugin.config?.({ ...config });
177
+ }
178
+
179
+ logger.debug({
180
+ group: 'parser',
181
+ task: 'config',
182
+ message: 'Finish config validation',
183
+ timing: performance.now() - configStart,
184
+ });
185
+
186
+ return config;
187
+ }
188
+
189
+ /**
190
+ * @param {object} a
191
+ * @param {object} b
192
+ * @return {object}
193
+ */
194
+ export function mergeConfigs(a, b) {
195
+ return merge(a, b);
196
+ }
package/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export { default as build } from './build/index.js';
2
+ export * from './build/index.js';
3
+
4
+ export { default as defineConfig } from './config.js';
5
+ export * from './config.js';
6
+
7
+ export { default as lintRunner } from './lint/index.js';
8
+ export * from './lint/index.js';
9
+
10
+ export { default as Logger } from './logger.js';
11
+ export * from './logger.js';
12
+
13
+ export { default as parse } from './parse/index.js';
14
+ export * from './parse/index.js';
15
+
16
+ export * from './types.js';
package/index.js ADDED
@@ -0,0 +1,37 @@
1
+ // MIT License
2
+ //
3
+ // Copyright (c) 2021 Drew Powers
4
+ //
5
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ // of this software and associated documentation files (the "Software"), to deal
7
+ // in the Software without restriction, including without limitation the rights
8
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ // copies of the Software, and to permit persons to whom the Software is
10
+ // furnished to do so, subject to the following conditions:
11
+ //
12
+ // The above copyright notice and this permission notice shall be included in all
13
+ // copies or substantial portions of the Software.
14
+ //
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ // SOFTWARE.
22
+ export { default as build } from './build/index.js';
23
+ export * from './build/index.js';
24
+
25
+ export { default as defineConfig } from './config.js';
26
+ export * from './config.js';
27
+
28
+ export { default as lintRunner } from './lint/index.js';
29
+ export * from './lint/index.js';
30
+
31
+ export { default as Logger } from './logger.js';
32
+ export * from './logger.js';
33
+
34
+ export { default as parse } from './parse/index.js';
35
+ export * from './parse/index.js';
36
+
37
+ export * from './types.js';
@@ -0,0 +1,41 @@
1
+ import type { AnyNode, DocumentNode } from '@humanwhocodes/momoa';
2
+ import type { ConfigInit } from '../config.js';
3
+ import type Logger from '../logger.js';
4
+ import type { Group } from '../types.js';
5
+
6
+ export interface LintNotice {
7
+ /** Lint message shown to the user */
8
+ message: string;
9
+ /** Erring node (used to point to a specific line) */
10
+ node?: AnyNode;
11
+ }
12
+
13
+ export type LintRuleSeverity = 'error' | 'warn' | 'off';
14
+ export type LintRuleShorthand = LintRuleSeverity | 0 | 1 | 2;
15
+ export type LintRuleLonghand = [LintRuleSeverity | 0 | 1 | 2, any];
16
+
17
+ export interface LintRuleNormalized<O = any> {
18
+ id: string;
19
+ severity: LintRuleSeverity;
20
+ options?: O;
21
+ }
22
+
23
+ export interface LinterOptions<O = any> {
24
+ tokens: Group;
25
+ rule: {
26
+ id: string;
27
+ severity: LintRuleSeverity;
28
+ };
29
+ ast: DocumentNode;
30
+ /** Any options the user has declared for this plugin */
31
+ options?: O;
32
+ }
33
+ export type Linter = (options: LinterOptions) => Promise<LintNotice[] | undefined>;
34
+
35
+ export interface LintRunnerOptions {
36
+ ast: DocumentNode;
37
+ config: ConfigInit;
38
+ logger: Logger;
39
+ }
40
+
41
+ export default function lintRunner(options: LintRunnerOptions): Promise<void>;