@wix/origami-lint 1.34.40 → 1.34.41
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/README.md +2 -1
- package/dist/rules/site-app/layout-design-notes.js +3 -1
- package/dist/rules/site-app/page-structure.js +45 -7
- package/dist/rules-api/component-catalog.js +31 -0
- package/dist/rules-api/rules-api.js +14 -6
- package/dist/shared/constants.js +3 -2
- package/dist/shared/paths.js +33 -1
- package/dist/shared/site-files-tree-mapping.js +2 -0
- package/dist/shared/utils.js +2 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,8 @@ dependencies and a `lint` script (`origami-lint`). It also writes the root `.git
|
|
|
14
14
|
git doesn't ignore) and runs `git init` at the end (the lint keeps the last passing version in the site's repository).
|
|
15
15
|
|
|
16
16
|
The lint runs only from the site's root: the folder it starts in is the site, and it stops unless that folder's
|
|
17
|
-
`package.json` has `@wix/origami-lint
|
|
17
|
+
`package.json` has `@wix/origami-lint`, the folder holds the git repository, and `packages/site-app/site-component-catalog.json`
|
|
18
|
+
(which the export writes: what each imported tag is, a component or a context provider) is there and is JSON.
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
21
|
yarn install # at the site's root
|
|
@@ -38,9 +38,11 @@ function checkElementLayout(input) {
|
|
|
38
38
|
checkBox(component, input);
|
|
39
39
|
if (type === 'innerElement')
|
|
40
40
|
checkInternalContainer(component, input);
|
|
41
|
-
if (
|
|
41
|
+
if (isTopElement(node, rulesApi))
|
|
42
42
|
checkMobileRules(input);
|
|
43
43
|
}
|
|
44
|
+
/** The page's top element: the root, or the element under the wrappers at the top (`<Remove>`, a context provider around `<Page>`). */
|
|
45
|
+
const isTopElement = (node, rulesApi) => !rulesApi.isWrapper(node) && getHolders(node, rulesApi).length === 0;
|
|
44
46
|
// ── a section and its canvas (KIT-sectionStack) ──
|
|
45
47
|
/** A section, or a section's canvas, with `display: flex`. */
|
|
46
48
|
function checkSectionIsGrid(component, what, input) {
|
|
@@ -17,16 +17,18 @@
|
|
|
17
17
|
// - an <InternalContainer>'s backgroundLayer / pinnedLayer, and a <Page>'s backgroundLayer / viewportLayer, is an array of elements
|
|
18
18
|
// (CONV-parseJsx.ts:1630);
|
|
19
19
|
// - a <PinnedToScreen> takes no props and holds one section, header or footer, in its <Page> slot: a child of the <Page>, or its
|
|
20
|
-
// header / footer. A pinned section is never in the viewportLayer, which holds the page's other pinned components
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// header / footer. A pinned section is never in the viewportLayer, which holds the page's other pinned components;
|
|
21
|
+
// - a context provider (a tag the site's component catalog lists under an app's contextProviders, converter 0.68) holds exactly one
|
|
22
|
+
// component element (CONV-parseJsx.ts:1028), sits outside <Remove> (CONV-parseJsx.ts:1237), and isn't inside a tag of the same
|
|
23
|
+
// provider: the component it wraps already has that context (CONV-parseJsx.ts:1176; converter 0.98 parseJsx.ts:1707, :1720).
|
|
24
|
+
// Wrappers are transparent for the tree, as they are for the import: a section wrapped in <Remove>, <PinnedToScreen> or a context
|
|
25
|
+
// provider is still the page's child, and a global component's section in a provider is still its top element.
|
|
23
26
|
// <Interaction> is no wrapper since converter 0.80 (interactions live in servicesConfigs/interactions.ts): allowed-imports
|
|
24
|
-
// reports the tag, and it stays transparent here so the elements inside it aren't reported too.
|
|
25
|
-
// once the page model lists the providers.
|
|
27
|
+
// reports the tag, and it stays transparent here so the elements inside it aren't reported too.
|
|
26
28
|
import { FILE_START } from "../../page-model/index.js";
|
|
27
29
|
import { getName, GLOBAL_SECTIONS, LAYER_ATTRIBUTES, SECTIONS, WRAPPERS } from "../../rules-api/index.js";
|
|
28
30
|
import { defineRule } from "./define-rule.js";
|
|
29
|
-
const CHECKS = ['missing_root', 'invalid_global_root', 'missing_root_id', 'invalid_page_root', 'non_element_child', 'invalid_section_container', 'nested_page', 'landmark_inside_container', 'invalid_wrapper_children', 'nested_remove', 'invalid_element_array', 'invalid_landmark_section', 'invalid_pinned_child', 'invalid_pinned_placement', 'pinned_wrapper_props', 'section_in_viewport_layer'];
|
|
31
|
+
const CHECKS = ['missing_root', 'invalid_global_root', 'missing_root_id', 'invalid_page_root', 'non_element_child', 'invalid_section_container', 'nested_page', 'landmark_inside_container', 'invalid_wrapper_children', 'nested_remove', 'invalid_element_array', 'invalid_landmark_section', 'invalid_pinned_child', 'invalid_pinned_placement', 'pinned_wrapper_props', 'section_in_viewport_layer', 'provider_in_remove', 'nested_provider'];
|
|
30
32
|
/** What <PinnedToScreen> may hold: a section, a header or footer, or a page's reference to a global one. */
|
|
31
33
|
const PINNABLE = [...SECTIONS, ...GLOBAL_SECTIONS, 'headerLandmark', 'footerLandmark'];
|
|
32
34
|
export default defineRule({
|
|
@@ -42,6 +44,8 @@ export default defineRule({
|
|
|
42
44
|
const type = input.rulesApi.getType(input.node);
|
|
43
45
|
if (type === 'pinnedToScreen')
|
|
44
46
|
checkPinnedToScreen(input);
|
|
47
|
+
if (type === 'provider')
|
|
48
|
+
checkProvider(input);
|
|
45
49
|
if (type !== 'remove')
|
|
46
50
|
return;
|
|
47
51
|
holdsOneElement(input);
|
|
@@ -227,7 +231,41 @@ function checkViewportLayerItems({ node, rulesApi, report }) {
|
|
|
227
231
|
report({ check: 'section_in_viewport_layer', loc: getLoc(element, rulesApi), message: `${describeElement(element, rulesApi)} is in the viewportLayer of ${describeElement(node, rulesApi)}. A pinned section, header or footer stays in its <Page> slot, wrapped in <PinnedToScreen>.` });
|
|
228
232
|
}
|
|
229
233
|
}
|
|
234
|
+
/** A context provider: one element held, outside <Remove>, not inside a tag of the same provider. */
|
|
235
|
+
function checkProvider(input) {
|
|
236
|
+
holdsOneElement(input);
|
|
237
|
+
checkProviderOutsideRemove(input);
|
|
238
|
+
checkNoProviderInSameProvider(input);
|
|
239
|
+
}
|
|
240
|
+
/** Reports a provider right inside a <Remove> (CONV-parseJsx.ts:1237): <Remove> wraps the component itself. Compared with the element right around it, as the import does. */
|
|
241
|
+
function checkProviderOutsideRemove({ node, rulesApi, report }) {
|
|
242
|
+
const outer = getAncestors(node)[0];
|
|
243
|
+
if (!outer || rulesApi.getType(outer) !== 'remove')
|
|
244
|
+
return;
|
|
245
|
+
report({ check: 'provider_in_remove', loc: node.openingElement.name.loc, message: `<${rulesApi.getTagName(node)}> is inside <${rulesApi.getTagName(outer)}>. <Remove> wraps the component itself: put the context provider outside it.` });
|
|
246
|
+
}
|
|
247
|
+
/** Reports a provider inside a tag of the same provider, among the wrappers around the same component (CONV-parseJsx.ts:1176): its context is already there. */
|
|
248
|
+
function checkNoProviderInSameProvider({ node, rulesApi, report }) {
|
|
249
|
+
const entry = rulesApi.getComponentCatalogEntry(node);
|
|
250
|
+
const outer = getWrappersAround(node, rulesApi).find((wrapper) => {
|
|
251
|
+
const outerEntry = rulesApi.getComponentCatalogEntry(wrapper);
|
|
252
|
+
return outerEntry !== null && entry !== null && outerEntry.app === entry.app && outerEntry.name === entry.name;
|
|
253
|
+
});
|
|
254
|
+
if (!outer)
|
|
255
|
+
return;
|
|
256
|
+
report({ check: 'nested_provider', loc: node.openingElement.name.loc, message: `<${rulesApi.getTagName(node)}> is inside <${rulesApi.getTagName(outer)}>, the same context provider: the component it wraps already has that context. Remove the inner one.` });
|
|
257
|
+
}
|
|
230
258
|
// ── reading the tree ──
|
|
259
|
+
/** The wrappers right around an element, closest first, up to the first component. */
|
|
260
|
+
function getWrappersAround(element, rulesApi) {
|
|
261
|
+
const wrappers = [];
|
|
262
|
+
for (const ancestor of getAncestors(element)) {
|
|
263
|
+
if (!isWrapper(ancestor, rulesApi))
|
|
264
|
+
break;
|
|
265
|
+
wrappers.push(ancestor);
|
|
266
|
+
}
|
|
267
|
+
return wrappers;
|
|
268
|
+
}
|
|
231
269
|
/** A section, header or footer, which <PinnedToScreen> may hold. */
|
|
232
270
|
const isPinnable = (element, rulesApi) => {
|
|
233
271
|
const type = rulesApi.getType(element);
|
|
@@ -249,7 +287,7 @@ function headerOrFooter(type) {
|
|
|
249
287
|
return 'footer';
|
|
250
288
|
return null;
|
|
251
289
|
}
|
|
252
|
-
/** <Remove>, <Interaction
|
|
290
|
+
/** <Remove>, <Interaction>, <PinnedToScreen>, a context provider: a tag that wraps one component and isn't one. */
|
|
253
291
|
function isWrapper(element, rulesApi) {
|
|
254
292
|
const type = rulesApi.getType(element);
|
|
255
293
|
return type !== null && WRAPPERS.includes(type);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The site component catalog the export writes (site-component-catalog.json): the site's apps, and the components, context
|
|
2
|
+
// providers and function libraries each one has. Read once per site and held as it is; rulesApi.getComponentCatalogEntry asks
|
|
3
|
+
// about one tag at a time, and getCatalogEntry looks that tag up in it.
|
|
4
|
+
import { readJsonFile } from "../shared/paths.js";
|
|
5
|
+
import { COMPONENT_CATALOG_FILE } from "../shared/site-files-tree-mapping.js";
|
|
6
|
+
/** An app's wrapper, as the export imports it: `@origami-wrappers/<app>/<folder>`, the folder its name lowercased (wrapperImport, origami-transforms). */
|
|
7
|
+
const WRAPPER_IMPORT = /^@origami-wrappers\/([^/]+)\/[^/]+$/;
|
|
8
|
+
/** The catalog's entry of a name imported from an app's wrapper, by its app and the name it's imported under:
|
|
9
|
+
* `import { CmsProvider_books } from '@origami-wrappers/WixCMS/cmsprovider_books'` → WixCMS's contextProviders.CmsProvider_books.
|
|
10
|
+
* null for an import from anywhere else, or a name the catalog doesn't list. */
|
|
11
|
+
export function getCatalogEntry(catalog, source, imported) {
|
|
12
|
+
const app = WRAPPER_IMPORT.exec(source)?.[1];
|
|
13
|
+
const entries = app === undefined ? undefined : catalog?.[app]?.entries;
|
|
14
|
+
if (app === undefined || !entries)
|
|
15
|
+
return null;
|
|
16
|
+
if (Object.hasOwn(entries.components ?? {}, imported))
|
|
17
|
+
return { app, name: imported, kind: 'component' };
|
|
18
|
+
if (Object.hasOwn(entries.contextProviders ?? {}, imported))
|
|
19
|
+
return { app, name: imported, kind: 'contextProvider' };
|
|
20
|
+
if (Object.hasOwn(entries.functionLibraries ?? {}, imported))
|
|
21
|
+
return { app, name: imported, kind: 'functionLibrary' };
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const catalogs = new Map();
|
|
25
|
+
/** The catalog of the site at root, read on the first call for that site; undefined when it has none. The lint stops before any
|
|
26
|
+
* page when the site has no catalog it can read (checkStartedAtSiteRoot). */
|
|
27
|
+
export function readComponentCatalog(root) {
|
|
28
|
+
if (!catalogs.has(root))
|
|
29
|
+
catalogs.set(root, readJsonFile(root, COMPONENT_CATALOG_FILE));
|
|
30
|
+
return catalogs.get(root);
|
|
31
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// The rules API of one linted file: its page, and the functions over it that the lint tool can't provide (mostly CSS),
|
|
2
2
|
// and reading the file's own code that more than one rule needs (what type a tag is, its name, its id, its slot, its children, the elements in its attributes, where a name is imported from, the name an import is exported under, what is TypeScript, the props an arrow destructures).
|
|
3
|
-
// One per file, shared by every rule on it. Only building its page reads from disk, once per file
|
|
3
|
+
// One per file, shared by every rule on it. Only building its page reads from disk, once per file; and the site's component catalog,
|
|
4
|
+
// once per site, when a rule first asks what a tag from an app's wrapper is.
|
|
4
5
|
import path from 'node:path';
|
|
6
|
+
import { getCatalogEntry, readComponentCatalog } from "./component-catalog.js";
|
|
5
7
|
import { GLOBAL_COMPONENTS_FOLDER } from "../shared/site-files-tree-mapping.js";
|
|
6
8
|
import { isCollectingStats, models, pageId } from "../bi/stats.js";
|
|
7
9
|
import { readPage } from "../page-model/index.js";
|
|
@@ -11,13 +13,18 @@ import { LAYER_ATTRIBUTES, WRAPPERS } from "../shared/constants.js";
|
|
|
11
13
|
import { getEnclosingAtRules, isBaseDeclaration } from "./utils.js";
|
|
12
14
|
/** Theme lines come from @apply inside style.css, so style.css's own lines usually come after them. */
|
|
13
15
|
const CASCADE_ORDER = ['layout', 'theme', 'style'];
|
|
14
|
-
/** The rules API over a page already built. */
|
|
15
|
-
export function createRulesApi(page) {
|
|
16
|
+
/** The rules API over a page already built. readCatalog: the site's component catalog, read when a rule first asks about a tag from an app's wrapper. */
|
|
17
|
+
export function createRulesApi(page, readCatalog = () => undefined) {
|
|
16
18
|
const componentsBySourceStart = new Map(Object.values(page.components).map((component) => [component.sourceStart, component]));
|
|
17
19
|
const getParent = (component) => (component.parentKey ? (page.components[component.parentKey] ?? null) : null);
|
|
18
20
|
const getCss = (component, property) => CASCADE_ORDER.flatMap((kind) => component.css[kind][property] ?? []);
|
|
21
|
+
const getComponentCatalogEntry = (element) => {
|
|
22
|
+
const found = getImport(getTagName(element).split('.')[0], getProgram(element));
|
|
23
|
+
return found?.kind === 'wrapper' ? getCatalogEntry(readCatalog(), found.source, found.imported) : null;
|
|
24
|
+
};
|
|
25
|
+
const getElementType = (element) => getType(element, getProgram(element)) ?? (getComponentCatalogEntry(element)?.kind === 'contextProvider' ? 'provider' : null);
|
|
19
26
|
const isWrapper = (element) => {
|
|
20
|
-
const type =
|
|
27
|
+
const type = getElementType(element);
|
|
21
28
|
return type !== null && WRAPPERS.includes(type);
|
|
22
29
|
};
|
|
23
30
|
const getChildElements = (node) => getChildren(node).flatMap((child) => {
|
|
@@ -44,8 +51,9 @@ export function createRulesApi(page) {
|
|
|
44
51
|
getCustomMedia: (kind) => readCustomMedia(page.css.files[kind]),
|
|
45
52
|
getBreakpointName: readBreakpointName,
|
|
46
53
|
getEnclosingAtRules,
|
|
47
|
-
getType:
|
|
54
|
+
getType: getElementType,
|
|
48
55
|
isWrapper,
|
|
56
|
+
getComponentCatalogEntry,
|
|
49
57
|
getTagName,
|
|
50
58
|
getId,
|
|
51
59
|
getChildren,
|
|
@@ -70,7 +78,7 @@ export function getOrCreateRulesApi(file) {
|
|
|
70
78
|
const started = performance.now();
|
|
71
79
|
let errorName;
|
|
72
80
|
try {
|
|
73
|
-
rulesApi = createRulesApi(readPage(file.cwd, { jsxFile: getRelativeJsxPath(file), text: file.text, program: file.program }));
|
|
81
|
+
rulesApi = createRulesApi(readPage(file.cwd, { jsxFile: getRelativeJsxPath(file), text: file.text, program: file.program }), () => readComponentCatalog(file.cwd));
|
|
74
82
|
rulesApis.set(file.filename, rulesApi);
|
|
75
83
|
}
|
|
76
84
|
catch (error) {
|
package/dist/shared/constants.js
CHANGED
|
@@ -6,8 +6,9 @@ export const CATALOG_FONTS = new Map(fonts.map((font) => [font.id, font]));
|
|
|
6
6
|
export const SECTIONS = ['section', 'header', 'footer'];
|
|
7
7
|
/** A page's reference to a global component (`<HeaderSection_x sharedCompId="…" />`). */
|
|
8
8
|
export const GLOBAL_SECTIONS = ['globalHeader', 'globalFooter', 'globalSection'];
|
|
9
|
-
/** A tag that wraps one component and isn't one itself: `<Remove when={…}>`, `<Interaction interactKey="…">`, `<PinnedToScreen>` (the import drops it)
|
|
10
|
-
|
|
9
|
+
/** A tag that wraps one component and isn't one itself: `<Remove when={…}>`, `<Interaction interactKey="…">`, `<PinnedToScreen>` (the import drops it),
|
|
10
|
+
* and a context provider (`<CmsProvider_books>`: the import makes it a context of the component it wraps). */
|
|
11
|
+
export const WRAPPERS = ['remove', 'interaction', 'pinnedToScreen', 'provider'];
|
|
11
12
|
/** The interactions packages, which the import reserves: structure.jsx imports nothing from them. Matched by source, not by name: `Interact` from @site/site-code is the site's own. */
|
|
12
13
|
export const INTERACTIONS_PACKAGES = ['interactReact', 'interact', 'motionPresets'];
|
|
13
14
|
/** The name a structure file exports its tree under: `export const Structure = () => (…)`. */
|
package/dist/shared/paths.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { COMPONENT_CATALOG_FILE } from "./site-files-tree-mapping.js";
|
|
3
4
|
/** The site: the folder the lint was started from (`yarn lint` at the site's root). */
|
|
4
5
|
export const ROOT = process.cwd();
|
|
5
6
|
export const LINT_DIR = path.join(ROOT, '.lint');
|
|
@@ -7,7 +8,8 @@ export const LINT_DIR = path.join(ROOT, '.lint');
|
|
|
7
8
|
export const LINT_PACKAGE_FILE = path.resolve(import.meta.dirname, '../../package.json');
|
|
8
9
|
const LINT_PACKAGE = 'origami-lint';
|
|
9
10
|
const LINT_PACKAGE_NAME = `@wix/${LINT_PACKAGE}`;
|
|
10
|
-
/** The lint runs only from the folder whose package.json adds it, which holds the site's git repository
|
|
11
|
+
/** The lint runs only from the folder whose package.json adds it, which holds the site's git repository and the component catalog the export
|
|
12
|
+
* wrote (what each imported tag is: a component, or a context provider). */
|
|
11
13
|
export function checkStartedAtSiteRoot() {
|
|
12
14
|
const problem = siteRootProblem();
|
|
13
15
|
if (!problem)
|
|
@@ -23,6 +25,36 @@ function siteRootProblem() {
|
|
|
23
25
|
if (!existsSync(path.join(ROOT, '.git'))) {
|
|
24
26
|
return { error_name: 'no_git', message: `no git repository in ${ROOT}. The lint keeps the last passing version in the site's repository: run git init here.` };
|
|
25
27
|
}
|
|
28
|
+
const catalog = readTextFile(ROOT, COMPONENT_CATALOG_FILE);
|
|
29
|
+
if (catalog === undefined) {
|
|
30
|
+
return { error_name: 'no_component_catalog', message: `${COMPONENT_CATALOG_FILE} not found: export the site again.` };
|
|
31
|
+
}
|
|
32
|
+
if (!isJson(catalog)) {
|
|
33
|
+
return { error_name: 'invalid_component_catalog', message: `${COMPONENT_CATALOG_FILE} isn't valid JSON: export the site again.` };
|
|
34
|
+
}
|
|
26
35
|
return null;
|
|
27
36
|
}
|
|
37
|
+
function isJson(text) {
|
|
38
|
+
try {
|
|
39
|
+
JSON.parse(text);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
28
46
|
const addsTheLint = ({ dependencies, devDependencies }) => LINT_PACKAGE_NAME in { ...dependencies, ...devDependencies };
|
|
47
|
+
/** A site file's text, by its path from the site's root; undefined when it doesn't exist. */
|
|
48
|
+
export function readTextFile(root, file) {
|
|
49
|
+
try {
|
|
50
|
+
return readFileSync(path.join(root, file), 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** A site JSON file, parsed, by its path from the site's root; undefined when it doesn't exist. Throws on a file that isn't JSON. */
|
|
57
|
+
export function readJsonFile(root, file) {
|
|
58
|
+
const text = readTextFile(root, file);
|
|
59
|
+
return text === undefined ? undefined : JSON.parse(text);
|
|
60
|
+
}
|
|
@@ -12,3 +12,5 @@ export const GLOBAL_COMPONENTS_FOLDER = `${SITE_SRC}/${GLOBAL_COMPONENTS}`;
|
|
|
12
12
|
/** Inside src/: one file per menu. A page imports a menu from here (`global/menus/<menu id>`). */
|
|
13
13
|
export const GLOBAL_MENUS = 'global/menus';
|
|
14
14
|
export const THEME_FILE = `${GLOBAL_FOLDER}/themes/theme.css`;
|
|
15
|
+
/** The site component catalog the export writes: the components, context providers and function libraries of the site's apps. */
|
|
16
|
+
export const COMPONENT_CATALOG_FILE = 'packages/site-app/site-component-catalog.json';
|
package/dist/shared/utils.js
CHANGED
|
@@ -32,7 +32,8 @@ const KIND_BY_IMPORT_SOURCE = [
|
|
|
32
32
|
[/^@origami-wrappers\//, 'wrapper'],
|
|
33
33
|
[/^@site\/site-code\/[^/]+$/, 'siteCode'],
|
|
34
34
|
];
|
|
35
|
-
/** <Band> with `import { Section as Band } from '@wix/origami-system-components/Section'` → 'section'. `<Band.Item>` is Band's. A wrapper is the named export: `<Show>` from the runtime is null.
|
|
35
|
+
/** <Band> with `import { Section as Band } from '@wix/origami-system-components/Section'` → 'section'. `<Band.Item>` is Band's. A wrapper is the named export: `<Show>` from the runtime is null.
|
|
36
|
+
* A context provider ('provider') is known from the site's component catalog: rulesApi.getType adds it. */
|
|
36
37
|
export function getType(element, program) {
|
|
37
38
|
const found = getImport(getTagName(element).split('.')[0], program);
|
|
38
39
|
const entry = found && TYPE_BY_IMPORT_SOURCE.find(([pattern]) => pattern.test(found.source));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/origami-lint",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.41",
|
|
4
4
|
"description": "Offline lint for an exported Origami site: lints the pages that changed since the last passing run",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"eslint": "^10.12.0",
|
|
27
27
|
"postcss": "^8.5.3",
|
|
28
28
|
"typescript": "^5.8.3 || ~6.0.3",
|
|
29
|
-
"@wix/origami-bi": "0.
|
|
29
|
+
"@wix/origami-bi": "0.17.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
]
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
"falconPackageHash": "
|
|
57
|
+
"falconPackageHash": "cd79205599adab600f2e5287c59ffd8f71d60a92c28f544cf5fe719f"
|
|
58
58
|
}
|