diff --git a/AGENTS.md b/AGENTS.md index 46aaab7ce..8d67e0c0d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ The root app (`src/`) is the Tauri/Vite desktop editor. App-specific editor, doc ### Public package exports -Use public package exports across package/app boundaries. Do not import workspace package internals from app code. +Use public package exports across package/app boundaries. Do not import workspace package internals from app code. Do not create cross-package re-export shim files whose only purpose is forwarding another package's API. Import the owning package directly at call sites; public compatibility barrels may re-export the owner directly when preserving an established package API. - `@open-pencil/scene-graph` — SceneGraph, node types, primitives, copy/snap/undo, instance helpers, variable helpers, vector-network types. - `@open-pencil/core` — broad compatibility barrel for editor/rendering/tooling APIs. diff --git a/CHANGELOG.md b/CHANGELOG.md index 94e87f6fc..f4447c27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ ### Changed -- Move complete `.fig` archive parsing and NodeChange-to-SceneGraph conversion policy into `@open-pencil/fig`, keeping `@open-pencil/kiwi` focused on Kiwi schema, message, and raw container mechanics. +- Move complete `.fig` archive parsing, NodeChange-to-SceneGraph conversion, and component/instance interpretation into `@open-pencil/fig`, keeping `@open-pencil/kiwi` focused on Kiwi schema, message, and raw container mechanics. +- Remove internal cross-package forwarding modules; import `@open-pencil/fig`, `@open-pencil/pen`, and `@open-pencil/scene-graph` from their owning public exports. - Add Figma-style page management in the Pages panel, including rename/delete actions and drag-and-drop page reordering. - Add DOM/CSS import and authoring support so HTML, CSS, Tailwind, and JSX can be converted into editable OpenPencil documents from the app, CLI, and SDK. - Add Tailwind class serialization for DOM/CSS HTML export in the SDK and CLI. diff --git a/packages/core/package.json b/packages/core/package.json index a08b540d2..b287b9acd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -100,11 +100,6 @@ "import": "./dist/io/formats/fig/index.js", "default": "./dist/io/formats/fig/index.js" }, - "./io/formats/pen": { - "types": "./dist/io/formats/pen/index.d.ts", - "import": "./dist/io/formats/pen/index.js", - "default": "./dist/io/formats/pen/index.js" - }, "./io/formats/jsx": { "types": "./dist/io/formats/jsx/index.d.ts", "import": "./dist/io/formats/jsx/index.js", diff --git a/packages/core/src/canvas/flatten.ts b/packages/core/src/canvas/flatten.ts index f935ea84a..1412c0e89 100644 --- a/packages/core/src/canvas/flatten.ts +++ b/packages/core/src/canvas/flatten.ts @@ -1,7 +1,6 @@ import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' import { copyFills } from '@open-pencil/scene-graph/copy' - -import { parseSVGPath } from '#core/io/formats/svg/parse-path' +import { parseSVGPath } from '@open-pencil/scene-graph/parse-path' import { makeBooleanSourcePath, makeStrokeOutlinePath, nodePathTransform } from './boolean' import type { SkiaRenderer } from './renderer' diff --git a/packages/core/src/clipboard.ts b/packages/core/src/clipboard.ts index b246fcfdd..695b58ff8 100644 --- a/packages/core/src/clipboard.ts +++ b/packages/core/src/clipboard.ts @@ -1,18 +1,18 @@ import { inflateSync, deflateSync } from 'fflate' +import { populateAndApplyOverrides } from '@open-pencil/fig/instance-overrides' +import type { InstanceNodeChange } from '@open-pencil/fig/instance-overrides' +import { + nodeChangeToProps, + shouldImportTextAsAutoSize, + sortChildren +} from '@open-pencil/fig/node-change' import { initCodec, getCompiledSchema, getSchemaBytes } from '@open-pencil/kiwi/fig/codec' import type { NodeChange as KiwiNodeChange } from '@open-pencil/kiwi/fig/codec' import { decodeBinarySchema, compileSchema, ByteBuffer } from '@open-pencil/kiwi/schema-runtime' import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' import { shapeTextForClipboard } from './canvas/text/clipboard' -import { populateAndApplyOverrides } from './kiwi/fig/instance-overrides' -import type { InstanceNodeChange } from './kiwi/fig/instance-overrides' -import { - nodeChangeToProps, - shouldImportTextAsAutoSize, - sortChildren -} from './kiwi/fig/node-change/convert' import { sceneNodeToKiwi, buildFigKiwi, diff --git a/packages/core/src/icons/svg.ts b/packages/core/src/icons/svg.ts index a6591ca22..572ed4db3 100644 --- a/packages/core/src/icons/svg.ts +++ b/packages/core/src/icons/svg.ts @@ -1,7 +1,7 @@ import { iconToSVG } from '@iconify/utils' import svgpath from 'svgpath' -import { parseSVGPath } from '#core/io/formats/svg/parse-path' +import { parseSVGPath } from '@open-pencil/scene-graph/parse-path' import type { IconData, IconifyIconEntry, IconPathInfo } from './types' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8a796b53d..73c4ba93c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -272,7 +272,7 @@ export { type SVGExportOptions } from './io/formats/svg/export' export { svg, renderSVGNode, type SVGNode } from './io/formats/svg/node' -export { parseSVGPath } from './io/formats/svg/parse-path' +export { parseSVGPath } from '@open-pencil/scene-graph/parse-path' export { fetchIcon, fetchIcons, @@ -371,7 +371,7 @@ export { } from './clipboard' export { probeGlyphOutlineCommands, type GlyphOutlineProbe } from './text/opentype' -export { readPenFile, parsePenFile } from './io/formats/pen' +export { readPenFile, parsePenFile } from '@open-pencil/pen' export { readFigFile, diff --git a/packages/core/src/io/formats.ts b/packages/core/src/io/formats.ts index d088e8f04..f16526d09 100644 --- a/packages/core/src/io/formats.ts +++ b/packages/core/src/io/formats.ts @@ -1,7 +1,8 @@ +import { parsePenFile } from '@open-pencil/pen' + import { sceneNodeToJSX, selectionToJSX } from '#core/design-jsx' import { exportFigFile, parseFigFile } from './formats/fig' -import { parsePenFile } from './formats/pen' import { headlessRenderNodes, renderNodesToImage, type RasterExportFormat } from './formats/raster' import { renderNodesToSVG } from './formats/svg' import { extractExportGraph, findPageId } from './subgraph' diff --git a/packages/core/src/io/formats/fig/compress.ts b/packages/core/src/io/formats/fig/compress.ts deleted file mode 100644 index b0824a8b2..000000000 --- a/packages/core/src/io/formats/fig/compress.ts +++ /dev/null @@ -1 +0,0 @@ -export { compressFigDataSync } from '@open-pencil/fig' diff --git a/packages/core/src/io/formats/fig/export-worker.ts b/packages/core/src/io/formats/fig/export-worker.ts index d3178a7de..837cceaa3 100644 --- a/packages/core/src/io/formats/fig/export-worker.ts +++ b/packages/core/src/io/formats/fig/export-worker.ts @@ -1,4 +1,4 @@ -import { compressFigDataSync } from './compress' +import { compressFigDataSync } from '@open-pencil/fig' interface CompressMessage { schemaDeflated: Uint8Array diff --git a/packages/core/src/io/formats/fig/export.ts b/packages/core/src/io/formats/fig/export.ts index d2e001762..254c31497 100644 --- a/packages/core/src/io/formats/fig/export.ts +++ b/packages/core/src/io/formats/fig/export.ts @@ -1,6 +1,8 @@ import type { CanvasKit } from 'canvaskit-wasm' import { deflateSync, inflateSync } from 'fflate' +import { compressFigDataSync } from '@open-pencil/fig' +import { stringToGuid } from '@open-pencil/fig/node-change' import { initCodec, getCompiledSchema, getSchemaBytes } from '@open-pencil/kiwi/fig/codec' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' import { decodeBinarySchema, compileSchema, ByteBuffer } from '@open-pencil/kiwi/schema-runtime' @@ -11,7 +13,6 @@ import type { SkiaRenderer } from '#core/canvas' import { CANVAS_BG_COLOR, IS_BROWSER, IS_TAURI } from '#core/constants' import { renderThumbnail } from '#core/io/formats/raster' import { populateAllLazyFigImportRoots } from '#core/kiwi/fig/lazy-import' -import { stringToGuid } from '#core/kiwi/fig/node-change/convert' import { sceneNodeToKiwi, fractionalPosition, @@ -21,8 +22,6 @@ import { makeCanvasNodeChange } from '#core/kiwi/fig/node-change/serialize' -import { compressFigDataSync } from './compress' - const THUMBNAIL_1X1 = Uint8Array.from( atob( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==' @@ -521,7 +520,7 @@ export async function exportFigFile( return compressFigData(schemaDeflated, kiwiData, thumbnailPng, metaJson, imageEntries, version) } -export { compressFigDataSync } from './compress' +export { compressFigDataSync } from '@open-pencil/fig' function canUseWorker(): boolean { return typeof Worker !== 'undefined' && IS_BROWSER diff --git a/packages/core/src/io/formats/pen/convert.ts b/packages/core/src/io/formats/pen/convert.ts deleted file mode 100644 index a6da212be..000000000 --- a/packages/core/src/io/formats/pen/convert.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@open-pencil/pen' diff --git a/packages/core/src/io/formats/pen/index.ts b/packages/core/src/io/formats/pen/index.ts deleted file mode 100644 index a6da212be..000000000 --- a/packages/core/src/io/formats/pen/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@open-pencil/pen' diff --git a/packages/core/src/io/formats/pen/read.ts b/packages/core/src/io/formats/pen/read.ts deleted file mode 100644 index d3c3f5789..000000000 --- a/packages/core/src/io/formats/pen/read.ts +++ /dev/null @@ -1 +0,0 @@ -export { parsePenFile, readPenFile } from '@open-pencil/pen' diff --git a/packages/core/src/io/formats/svg/parse-path.ts b/packages/core/src/io/formats/svg/parse-path.ts deleted file mode 100644 index 63f9a2ce1..000000000 --- a/packages/core/src/io/formats/svg/parse-path.ts +++ /dev/null @@ -1 +0,0 @@ -export { parseSVGPath } from '@open-pencil/scene-graph/parse-path' diff --git a/packages/core/src/io/index.ts b/packages/core/src/io/index.ts index e9e8a7b49..43a1206c0 100644 --- a/packages/core/src/io/index.ts +++ b/packages/core/src/io/index.ts @@ -11,7 +11,7 @@ export { jsxFormat } from './formats' export { exportFigFile, parseFigFile, readFigFile } from './formats/fig' -export { parsePenFile, readPenFile } from './formats/pen' +export { parsePenFile, readPenFile } from '@open-pencil/pen' export { sceneNodeToJSX, selectionToJSX, type JSXFormat } from './formats/jsx' export { computeContentBounds, diff --git a/packages/core/src/kiwi/fig/import.ts b/packages/core/src/kiwi/fig/import.ts index 6ce5f2af6..ddad637a8 100644 --- a/packages/core/src/kiwi/fig/import.ts +++ b/packages/core/src/kiwi/fig/import.ts @@ -1,22 +1,22 @@ import { isNotNil } from 'es-toolkit/predicate' -import { applyStyleRefsToFields } from '@open-pencil/fig/node-change' -import type { NodeChange, VariableDataValuesEntry, Color, GUID } from '@open-pencil/kiwi/fig/codec' -import { SceneGraph } from '@open-pencil/scene-graph' -import type { VariableType, VariableValue } from '@open-pencil/scene-graph' - -import { BLACK } from '#core/constants' -import { populateAndApplyOverrides } from '#core/kiwi/fig/instance-overrides' -import type { InstanceNodeChange } from '#core/kiwi/fig/instance-overrides' -import { setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' +import { populateAndApplyOverrides } from '@open-pencil/fig/instance-overrides' +import type { InstanceNodeChange } from '@open-pencil/fig/instance-overrides' import { + applyStyleRefsToFields, guidToString, nodeChangeToProps, shouldImportTextAsAutoSize, sortChildren, setVariableColorResolver, VARIABLE_BINDING_FIELDS_INVERSE -} from '#core/kiwi/fig/node-change/convert' +} from '@open-pencil/fig/node-change' +import type { NodeChange, VariableDataValuesEntry, Color, GUID } from '@open-pencil/kiwi/fig/codec' +import { SceneGraph } from '@open-pencil/scene-graph' +import type { VariableType, VariableValue } from '@open-pencil/scene-graph' + +import { BLACK } from '#core/constants' +import { setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' type AssetRef = { key: string; version?: string } type AliasRef = { guid?: GUID; assetRef?: AssetRef } diff --git a/packages/core/src/kiwi/fig/lazy-import.ts b/packages/core/src/kiwi/fig/lazy-import.ts index d9b252a19..0e42fa345 100644 --- a/packages/core/src/kiwi/fig/lazy-import.ts +++ b/packages/core/src/kiwi/fig/lazy-import.ts @@ -1,8 +1,7 @@ +import { populateAndApplyOverrides } from '@open-pencil/fig/instance-overrides' +import type { InstanceNodeChange } from '@open-pencil/fig/instance-overrides' import type { SceneGraph } from '@open-pencil/scene-graph' -import { populateAndApplyOverrides } from '#core/kiwi/fig/instance-overrides' -import type { InstanceNodeChange } from '#core/kiwi/fig/instance-overrides' - export interface LazyFigImportContext { changeMap: Map guidToNodeId: Map diff --git a/packages/core/src/kiwi/fig/node-change/convert.ts b/packages/core/src/kiwi/fig/node-change/convert.ts deleted file mode 100644 index 88cc15b99..000000000 --- a/packages/core/src/kiwi/fig/node-change/convert.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@open-pencil/fig/node-change' diff --git a/packages/core/src/kiwi/fig/node-change/serialize.ts b/packages/core/src/kiwi/fig/node-change/serialize.ts index cd64fbffe..1ac8349fb 100644 --- a/packages/core/src/kiwi/fig/node-change/serialize.ts +++ b/packages/core/src/kiwi/fig/node-change/serialize.ts @@ -16,15 +16,17 @@ export { buildFontDigestMap } from './font/digests' import { applyFontFeaturesToKiwi, BOUND_VARIABLES_PLUGIN_KEY, + guidToString, LAYOUT_DIRECTION_PLUGIN_KEY, + stringToGuid, TEXT_DIRECTION_PLUGIN_KEY, - upsertPluginData + upsertPluginData, + VARIABLE_BINDING_FIELDS } from '@open-pencil/fig/node-change' import type { NodeChange, Paint, VariableConsumptionEntry } from '@open-pencil/kiwi/fig/codec' import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' import type { Color, GUID, JsonObject, Matrix } from '@open-pencil/scene-graph/primitives' -import { guidToString, stringToGuid, VARIABLE_BINDING_FIELDS } from './convert' import { buildAssetRefToVarGuidMap, sceneNodeToKiwiWithContext, diff --git a/packages/core/src/kiwi/fig/node-change/style-runs.ts b/packages/core/src/kiwi/fig/node-change/style-runs.ts deleted file mode 100644 index 0c28c41af..000000000 --- a/packages/core/src/kiwi/fig/node-change/style-runs.ts +++ /dev/null @@ -1 +0,0 @@ -export { importStyleRuns } from '@open-pencil/fig/node-change' diff --git a/packages/core/src/kiwi/fig/parse/transfer.ts b/packages/core/src/kiwi/fig/parse/transfer.ts index 054e8f0b6..b8bed5bfb 100644 --- a/packages/core/src/kiwi/fig/parse/transfer.ts +++ b/packages/core/src/kiwi/fig/parse/transfer.ts @@ -1,3 +1,4 @@ +import type { InstanceNodeChange } from '@open-pencil/fig/instance-overrides' import { SceneGraph } from '@open-pencil/scene-graph' import type { SceneNode, @@ -6,7 +7,6 @@ import type { DocumentColorSpace } from '@open-pencil/scene-graph' -import type { InstanceNodeChange } from '#core/kiwi/fig/instance-overrides' import { getLazyFigImportContext, setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' export interface SerializedLazyFigImportContext { diff --git a/packages/core/src/tools/create/svg.ts b/packages/core/src/tools/create/svg.ts index 9d771b396..baf7f8aee 100644 --- a/packages/core/src/tools/create/svg.ts +++ b/packages/core/src/tools/create/svg.ts @@ -1,10 +1,10 @@ +import { parseSVGPath } from '@open-pencil/scene-graph/parse-path' import type { Rect } from '@open-pencil/scene-graph/primitives' import { parseColor } from '#core/color' import { createPathStroke } from '#core/icons/path-style' import { extractPaths } from '#core/icons/svg' import type { IconPathInfo } from '#core/icons/types' -import { parseSVGPath } from '#core/io/formats/svg/parse-path' import { defineTool } from '#core/tools/schema' function parseSvgViewBox(svg: string): Rect | null { diff --git a/packages/fig/README.md b/packages/fig/README.md index b35ad9796..9e01d550a 100644 --- a/packages/fig/README.md +++ b/packages/fig/README.md @@ -14,13 +14,13 @@ Current ownership: - `readFigContainer()` / `writeFigContainer()` helpers for raw `fig-kiwi` payloads - `.fig` source and archive result types - NodeChange-to-SceneGraph property conversion, including styles, plugin metadata, text, paint, vector, and font policy, through `@open-pencil/fig/node-change` -- Package-local archive, conversion, and dist smoke tests +- Component-property, symbol-override, derived-symbol-data, and instance synchronization policy through `@open-pencil/fig/instance-overrides` +- Package-local archive, conversion, instance, and dist smoke tests Planned ownership: - Remaining SceneGraph-to-`NodeChange` export conversion and document orchestration - Raw Figma metadata precedence and invalidation policy -- Component and instance interpretation - Oracle-backed `.fig` fixtures Non-goals: diff --git a/packages/fig/package.json b/packages/fig/package.json index 864b8ca4e..0b41c77f2 100644 --- a/packages/fig/package.json +++ b/packages/fig/package.json @@ -15,6 +15,12 @@ "bun": "./src/node-change/index.ts", "import": "./dist/node-change.js", "default": "./dist/node-change.js" + }, + "./instance-overrides": { + "types": "./dist/instance-overrides.d.ts", + "bun": "./src/instance-overrides/index.ts", + "import": "./dist/instance-overrides.js", + "default": "./dist/instance-overrides.js" } }, "main": "./dist/index.js", diff --git a/packages/fig/scripts/smoke-dist.ts b/packages/fig/scripts/smoke-dist.ts index eb42004f6..afaa78035 100644 --- a/packages/fig/scripts/smoke-dist.ts +++ b/packages/fig/scripts/smoke-dist.ts @@ -1,12 +1,14 @@ export {} const mod = await import('../dist/index.js') +const instanceOverrides = await import('../dist/instance-overrides.js') const nodeChange = await import('../dist/node-change.js') if ( mod.FIG_PACKAGE_STATUS !== 'archive-api' || typeof mod.parseFigBuffer !== 'function' || typeof mod.writeFigArchive !== 'function' || + typeof instanceOverrides.populateAndApplyOverrides !== 'function' || typeof nodeChange.convertLineHeight !== 'function' ) { throw new Error('Expected @open-pencil/fig archive API exports') diff --git a/packages/core/src/kiwi/fig/instance-overrides/clone-index.ts b/packages/fig/src/instance-overrides/clone-index.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/clone-index.ts rename to packages/fig/src/instance-overrides/clone-index.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts b/packages/fig/src/instance-overrides/component-props/apply.ts similarity index 87% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts rename to packages/fig/src/instance-overrides/component-props/apply.ts index 9bfd984e5..ae3048bfb 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts +++ b/packages/fig/src/instance-overrides/component-props/apply.ts @@ -1,14 +1,9 @@ +import { guidToString } from '@open-pencil/fig/node-change' import { copyFills, copyStyleRuns } from '@open-pencil/scene-graph/copy' -import { applyOverridePatch, type OverridePatch } from '#core/kiwi/fig/instance-overrides/patches' -import { getComponentRoot } from '#core/kiwi/fig/instance-overrides/resolve' -import type { - ComponentPropRef, - ComponentPropValue, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' -import { guidToString } from '#core/kiwi/fig/node-change/convert' - +import { applyOverridePatch, type OverridePatch } from '../patches' +import { getComponentRoot } from '../resolve' +import type { ComponentPropRef, ComponentPropValue, OverrideContext } from '../types' import { propTextCharacters } from './values' function applyPatchAndMark( diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/assignments.ts b/packages/fig/src/instance-overrides/component-props/assignments.ts similarity index 88% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/assignments.ts rename to packages/fig/src/instance-overrides/component-props/assignments.ts index 5b59c6858..aca69cac6 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/assignments.ts +++ b/packages/fig/src/instance-overrides/component-props/assignments.ts @@ -1,20 +1,16 @@ import type { SceneNode } from '@open-pencil/scene-graph' -import { buildCloneIndex, instanceAndClones } from '#core/kiwi/fig/instance-overrides/clone-index' -import { applyComponentPropRef } from '#core/kiwi/fig/instance-overrides/component-props/apply' -import { - fallbackRefsForChild, - findPropRefs, - valueForRef -} from '#core/kiwi/fig/instance-overrides/component-props/refs' -import { assignmentsToValueMap } from '#core/kiwi/fig/instance-overrides/component-props/values' -import { resolveOverrideTarget } from '#core/kiwi/fig/instance-overrides/resolve' +import { buildCloneIndex, instanceAndClones } from '../clone-index' +import { resolveOverrideTarget } from '../resolve' import type { ComponentPropAssignment, ComponentPropRef, ComponentPropValue, OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' +} from '../types' +import { applyComponentPropRef } from './apply' +import { fallbackRefsForChild, findPropRefs, valueForRef } from './refs' +import { assignmentsToValueMap } from './values' function applyChildPropRefs( ctx: OverrideContext, diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts b/packages/fig/src/instance-overrides/component-props/index.ts similarity index 90% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts rename to packages/fig/src/instance-overrides/component-props/index.ts index 038e21f00..88de5c16e 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts +++ b/packages/fig/src/instance-overrides/component-props/index.ts @@ -1,5 +1,4 @@ -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides/types' - +import type { OverrideContext } from '../types' import { applyInstanceDirectAssignments, applyOverrideAssignments } from './assignments' import { collectAssignmentsMap, collectPropRefsMap } from './maps' diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/maps.ts b/packages/fig/src/instance-overrides/component-props/maps.ts similarity index 83% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/maps.ts rename to packages/fig/src/instance-overrides/component-props/maps.ts index fbfdc4b32..bb8d8c993 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/maps.ts +++ b/packages/fig/src/instance-overrides/component-props/maps.ts @@ -1,8 +1,4 @@ -import type { - ComponentPropAssignment, - ComponentPropRef, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' +import type { ComponentPropAssignment, ComponentPropRef, OverrideContext } from '../types' export function collectPropRefsMap(ctx: OverrideContext): Map { const result = new Map() diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/refs.ts b/packages/fig/src/instance-overrides/component-props/refs.ts similarity index 89% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/refs.ts rename to packages/fig/src/instance-overrides/component-props/refs.ts index 5f527e0c2..279c54c13 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/refs.ts +++ b/packages/fig/src/instance-overrides/component-props/refs.ts @@ -1,10 +1,6 @@ -import type { - ComponentPropRef, - ComponentPropValue, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' -import { guidToString } from '#core/kiwi/fig/node-change/convert' +import { guidToString } from '@open-pencil/fig/node-change' +import type { ComponentPropRef, ComponentPropValue, OverrideContext } from '../types' import { normalizePropName, stringToGuidParts } from './values' export function findPropRefs( diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts b/packages/fig/src/instance-overrides/component-props/values.ts similarity index 91% rename from packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts rename to packages/fig/src/instance-overrides/component-props/values.ts index 5337679bf..4b64513b5 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts +++ b/packages/fig/src/instance-overrides/component-props/values.ts @@ -1,11 +1,7 @@ +import { guidToString } from '@open-pencil/fig/node-change' import type { GUID } from '@open-pencil/kiwi/fig/codec' -import type { - ComponentPropAssignment, - ComponentPropValue, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' -import { guidToString } from '#core/kiwi/fig/node-change/convert' +import type { ComponentPropAssignment, ComponentPropValue, OverrideContext } from '../types' export function normalizePropName(value: string): string { return value.toLowerCase().replace(/[^a-z0-9]/g, '') diff --git a/packages/core/src/kiwi/fig/instance-overrides/constraints.ts b/packages/fig/src/instance-overrides/constraints.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/constraints.ts rename to packages/fig/src/instance-overrides/constraints.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts b/packages/fig/src/instance-overrides/derived-symbol-data/geometry.ts similarity index 92% rename from packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts rename to packages/fig/src/instance-overrides/derived-symbol-data/geometry.ts index 6d2d80a6b..f2c68194b 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts +++ b/packages/fig/src/instance-overrides/derived-symbol-data/geometry.ts @@ -1,7 +1,7 @@ +import { resolveGeometryPaths } from '@open-pencil/fig/node-change' import type { GeometryPath, SceneNode } from '@open-pencil/scene-graph' -import type { DerivedSymbolOverride } from '#core/kiwi/fig/instance-overrides/types' -import { resolveGeometryPaths } from '#core/kiwi/fig/node-change/convert' +import type { DerivedSymbolOverride } from '../types' function scaleGeometryBlobs(geom: GeometryPath[], sx: number, sy: number): GeometryPath[] { if (sx === 1 && sy === 1) return geom diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts b/packages/fig/src/instance-overrides/derived-symbol-data/index.ts similarity index 86% rename from packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts rename to packages/fig/src/instance-overrides/derived-symbol-data/index.ts index e42a21cb6..ec31e0b70 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts +++ b/packages/fig/src/instance-overrides/derived-symbol-data/index.ts @@ -1,10 +1,6 @@ -import { applyOverridePatch } from '#core/kiwi/fig/instance-overrides/patches' -import { resolveOverrideTarget } from '#core/kiwi/fig/instance-overrides/resolve' -import type { - DerivedSymbolOverride, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' - +import { applyOverridePatch } from '../patches' +import { resolveOverrideTarget } from '../resolve' +import type { DerivedSymbolOverride, OverrideContext } from '../types' import { buildDsdLayoutUpdates } from './layout' import { propagateDsdChanges } from './propagate' diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts b/packages/fig/src/instance-overrides/derived-symbol-data/layout.ts similarity index 92% rename from packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts rename to packages/fig/src/instance-overrides/derived-symbol-data/layout.ts index 09f26fbde..fd8339451 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts +++ b/packages/fig/src/instance-overrides/derived-symbol-data/layout.ts @@ -1,12 +1,11 @@ -import { convertFigmaDerivedTextGlyphs } from '@open-pencil/fig/node-change' +import { + convertFigmaDerivedTextGlyphs, + convertLetterSpacing, + convertLineHeight +} from '@open-pencil/fig/node-change' import type { SceneNode } from '@open-pencil/scene-graph' -import type { - DerivedSymbolOverride, - OverrideContext -} from '#core/kiwi/fig/instance-overrides/types' -import { convertLetterSpacing, convertLineHeight } from '#core/kiwi/fig/node-change/convert' - +import type { DerivedSymbolOverride, OverrideContext } from '../types' import { resolveDsdGeometry } from './geometry' function getVisibleSiblingCount( diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts b/packages/fig/src/instance-overrides/derived-symbol-data/propagate.ts similarity index 93% rename from packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts rename to packages/fig/src/instance-overrides/derived-symbol-data/propagate.ts index 9dd66836e..48b5af8c5 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts +++ b/packages/fig/src/instance-overrides/derived-symbol-data/propagate.ts @@ -1,8 +1,8 @@ import type { SceneNode } from '@open-pencil/scene-graph' import { copyGeometryPaths } from '@open-pencil/scene-graph/copy' -import { buildClonesMap } from '#core/kiwi/fig/instance-overrides/sync' -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides/types' +import { buildClonesMap } from '../sync' +import type { OverrideContext } from '../types' function buildCloneUpdates( ctx: OverrideContext, diff --git a/packages/core/src/kiwi/fig/instance-overrides/index.ts b/packages/fig/src/instance-overrides/index.ts similarity index 96% rename from packages/core/src/kiwi/fig/instance-overrides/index.ts rename to packages/fig/src/instance-overrides/index.ts index df3dde039..a41513b96 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/index.ts +++ b/packages/fig/src/instance-overrides/index.ts @@ -1,3 +1,7 @@ +export { buildDsdLayoutUpdates } from './derived-symbol-data/layout' +export { propagateDsdChanges } from './derived-symbol-data/propagate' +export { protectField, type ProtectionMap } from './patches' +export { syncChildrenDeep, syncNodeProps } from './sync' export type { InstanceNodeChange, OverrideContext, @@ -10,12 +14,11 @@ export type { SymbolOverride } from './types' +import { guidToString } from '@open-pencil/fig/node-change' import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' import { copyFills, copyStyleRuns } from '@open-pencil/scene-graph/copy' import type { JsonObject } from '@open-pencil/scene-graph/primitives' -import { guidToString } from '#core/kiwi/fig/node-change/convert' - import { applyComponentProperties } from './component-props' import { applyConstraintScaling } from './constraints' import { applyDerivedSymbolData } from './derived-symbol-data' diff --git a/packages/core/src/kiwi/fig/instance-overrides/patches/apply.ts b/packages/fig/src/instance-overrides/patches/apply.ts similarity index 90% rename from packages/core/src/kiwi/fig/instance-overrides/patches/apply.ts rename to packages/fig/src/instance-overrides/patches/apply.ts index 16758414d..913badecb 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/patches/apply.ts +++ b/packages/fig/src/instance-overrides/patches/apply.ts @@ -1,8 +1,7 @@ import type { SceneNode } from '@open-pencil/scene-graph' -import { repopulateInstance } from '#core/kiwi/fig/instance-overrides/resolve' -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides/types' - +import { repopulateInstance } from '../resolve' +import type { OverrideContext } from '../types' import { protectField, protectPatchProps } from './protection' import type { OverridePatch } from './types' diff --git a/packages/core/src/kiwi/fig/instance-overrides/patches/index.ts b/packages/fig/src/instance-overrides/patches/index.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/patches/index.ts rename to packages/fig/src/instance-overrides/patches/index.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/patches/protection.ts b/packages/fig/src/instance-overrides/patches/protection.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/patches/protection.ts rename to packages/fig/src/instance-overrides/patches/protection.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/patches/types.ts b/packages/fig/src/instance-overrides/patches/types.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/patches/types.ts rename to packages/fig/src/instance-overrides/patches/types.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/populate.ts b/packages/fig/src/instance-overrides/populate.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/populate.ts rename to packages/fig/src/instance-overrides/populate.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/resolve.ts b/packages/fig/src/instance-overrides/resolve.ts similarity index 99% rename from packages/core/src/kiwi/fig/instance-overrides/resolve.ts rename to packages/fig/src/instance-overrides/resolve.ts index f89c36d5f..bdd96aa00 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/resolve.ts +++ b/packages/fig/src/instance-overrides/resolve.ts @@ -1,9 +1,8 @@ +import { guidToString } from '@open-pencil/fig/node-change' import type { GUID } from '@open-pencil/kiwi/fig/codec' import type { SceneNode } from '@open-pencil/scene-graph' import { copyStrokes } from '@open-pencil/scene-graph/copy' -import { guidToString } from '#core/kiwi/fig/node-change/convert' - import type { InstanceNodeChange, OverrideContext } from './types' const MAX_CHAIN_DEPTH = 20 diff --git a/packages/core/src/kiwi/fig/instance-overrides/symbol/overrides.ts b/packages/fig/src/instance-overrides/symbol/overrides.ts similarity index 85% rename from packages/core/src/kiwi/fig/instance-overrides/symbol/overrides.ts rename to packages/fig/src/instance-overrides/symbol/overrides.ts index 7d26be784..280aa644b 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/symbol/overrides.ts +++ b/packages/fig/src/instance-overrides/symbol/overrides.ts @@ -1,7 +1,6 @@ -import { applyOverridePatch } from '#core/kiwi/fig/instance-overrides/patches' -import { resolveOverrideTarget } from '#core/kiwi/fig/instance-overrides/resolve' -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides/types' - +import { applyOverridePatch } from '../patches' +import { resolveOverrideTarget } from '../resolve' +import type { OverrideContext } from '../types' import { patchFromSymbolOverride } from './patches' function isActiveInstance(ctx: OverrideContext, nodeId: string | undefined): nodeId is string { diff --git a/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts b/packages/fig/src/instance-overrides/symbol/patches.ts similarity index 91% rename from packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts rename to packages/fig/src/instance-overrides/symbol/patches.ts index aa4cd4cce..27d6e4169 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/symbol/patches.ts +++ b/packages/fig/src/instance-overrides/symbol/patches.ts @@ -1,14 +1,12 @@ -import { applyStyleRefsToFields } from '@open-pencil/fig/node-change' +import { + applyStyleRefsToFields, + guidToString, + VARIABLE_BINDING_FIELDS_INVERSE +} from '@open-pencil/fig/node-change' import type { GUID } from '@open-pencil/kiwi/fig/codec' -import type { OverridePatch } from '#core/kiwi/fig/instance-overrides/patches' -import type { - OverrideContext, - SymbolOverride, - SymbolOverrideFields -} from '#core/kiwi/fig/instance-overrides/types' -import { guidToString, VARIABLE_BINDING_FIELDS_INVERSE } from '#core/kiwi/fig/node-change/convert' - +import type { OverridePatch } from '../patches' +import type { OverrideContext, SymbolOverride, SymbolOverrideFields } from '../types' import { convertOverrideToProps } from './props' interface AliasRef { diff --git a/packages/core/src/kiwi/fig/instance-overrides/symbol/props.ts b/packages/fig/src/instance-overrides/symbol/props.ts similarity index 98% rename from packages/core/src/kiwi/fig/instance-overrides/symbol/props.ts rename to packages/fig/src/instance-overrides/symbol/props.ts index 8b700fb52..6a282ac06 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/symbol/props.ts +++ b/packages/fig/src/instance-overrides/symbol/props.ts @@ -1,7 +1,3 @@ -import type { NodeChange, Paint, Effect as KiwiEffect } from '@open-pencil/kiwi/fig/codec' -import type { SceneNode, ArcData, TextAutoResize } from '@open-pencil/scene-graph' -import type { Vector } from '@open-pencil/scene-graph/primitives' - import { convertFills, mapStackSizing, @@ -15,8 +11,11 @@ import { importStyleRuns, convertStrokes, convertEffects -} from '#core/kiwi/fig/node-change/convert' -import { styleToWeight } from '#core/text/fonts' +} from '@open-pencil/fig/node-change' +import type { NodeChange, Paint, Effect as KiwiEffect } from '@open-pencil/kiwi/fig/codec' +import type { SceneNode, ArcData, TextAutoResize } from '@open-pencil/scene-graph' +import { styleToWeight } from '@open-pencil/scene-graph' +import type { Vector } from '@open-pencil/scene-graph/primitives' function applyOverridePaints(ov: Record, updates: Partial): void { if (ov.textData != null) { diff --git a/packages/core/src/kiwi/fig/instance-overrides/sync/clones.ts b/packages/fig/src/instance-overrides/sync/clones.ts similarity index 96% rename from packages/core/src/kiwi/fig/instance-overrides/sync/clones.ts rename to packages/fig/src/instance-overrides/sync/clones.ts index aff320919..41e4ba22a 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/sync/clones.ts +++ b/packages/fig/src/instance-overrides/sync/clones.ts @@ -1,7 +1,6 @@ import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' -import type { ProtectionMap } from '#core/kiwi/fig/instance-overrides/patches' - +import type { ProtectionMap } from '../patches' import { syncNodeProps } from './fields' export function recloneChildren( diff --git a/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts b/packages/fig/src/instance-overrides/sync/fields.ts similarity index 95% rename from packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts rename to packages/fig/src/instance-overrides/sync/fields.ts index 3b8eb0d03..7bca8673e 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts +++ b/packages/fig/src/instance-overrides/sync/fields.ts @@ -1,8 +1,8 @@ import type { SceneGraph, SceneNode } from '@open-pencil/scene-graph' import { copyFills, copyStrokes, copyEffects, copyStyleRuns } from '@open-pencil/scene-graph/copy' -import type { ProtectionMap, ProtectedField } from '#core/kiwi/fig/instance-overrides/patches' -import { isFieldProtected } from '#core/kiwi/fig/instance-overrides/patches' +import { isFieldProtected } from '../patches' +import type { ProtectedField, ProtectionMap } from '../patches' function canSync( protections: ProtectionMap | undefined, diff --git a/packages/core/src/kiwi/fig/instance-overrides/sync/index.ts b/packages/fig/src/instance-overrides/sync/index.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/sync/index.ts rename to packages/fig/src/instance-overrides/sync/index.ts diff --git a/packages/core/src/kiwi/fig/instance-overrides/sync/propagate.ts b/packages/fig/src/instance-overrides/sync/propagate.ts similarity index 97% rename from packages/core/src/kiwi/fig/instance-overrides/sync/propagate.ts rename to packages/fig/src/instance-overrides/sync/propagate.ts index e69d5768f..62393fb9e 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/sync/propagate.ts +++ b/packages/fig/src/instance-overrides/sync/propagate.ts @@ -1,7 +1,6 @@ import type { SceneGraph } from '@open-pencil/scene-graph' -import type { ProtectionMap } from '#core/kiwi/fig/instance-overrides/patches' - +import type { ProtectionMap } from '../patches' import { buildClonesMap, syncChildrenDeep } from './clones' import { syncNodeProps } from './fields' diff --git a/packages/core/src/kiwi/fig/instance-overrides/types.ts b/packages/fig/src/instance-overrides/types.ts similarity index 100% rename from packages/core/src/kiwi/fig/instance-overrides/types.ts rename to packages/fig/src/instance-overrides/types.ts diff --git a/packages/fig/tests/instance-overrides.test.ts b/packages/fig/tests/instance-overrides.test.ts new file mode 100644 index 000000000..e83b089d8 --- /dev/null +++ b/packages/fig/tests/instance-overrides.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from 'bun:test' + +import { SceneGraph } from '@open-pencil/scene-graph' + +import { + populateAndApplyOverrides, + protectField, + syncNodeProps, + type ProtectionMap +} from '../src/instance-overrides' + +describe('@open-pencil/fig instance interpretation', () => { + test('populates an empty instance from its component tree', () => { + const graph = new SceneGraph() + const pageId = graph.getPages()[0].id + const component = graph.createNode('COMPONENT', pageId, { name: 'Button' }) + graph.createNode('TEXT', component.id, { text: 'Label' }) + const instance = graph.createNode('INSTANCE', pageId, { + componentId: component.id, + childIds: [] + }) + + populateAndApplyOverrides(graph, new Map(), new Map()) + + const populated = graph.getNode(instance.id) + expect(populated?.childIds).toHaveLength(1) + expect(graph.getNode(populated?.childIds[0] ?? '')?.text).toBe('Label') + }) + + test('preserves protected text while synchronizing other fields', () => { + const graph = new SceneGraph() + const pageId = graph.getPages()[0].id + const source = graph.createNode('TEXT', pageId, { text: 'Source', visible: false }) + const target = graph.createNode('TEXT', pageId, { text: 'Override', visible: true }) + const protections: ProtectionMap = new Map() + protectField(protections, target.id, 'text') + + syncNodeProps(graph, source, target, protections) + + expect(graph.getNode(target.id)).toMatchObject({ text: 'Override', visible: false }) + }) +}) diff --git a/packages/fig/tsconfig.json b/packages/fig/tsconfig.json index 7143ab4be..ca29f4cfb 100644 --- a/packages/fig/tsconfig.json +++ b/packages/fig/tsconfig.json @@ -15,6 +15,7 @@ "types": ["bun"], "paths": { "@open-pencil/fig": ["./src/index.ts"], + "@open-pencil/fig/instance-overrides": ["./src/instance-overrides/index.ts"], "@open-pencil/fig/node-change": ["./src/node-change/index.ts"] } }, diff --git a/packages/fig/tsdown.config.ts b/packages/fig/tsdown.config.ts index 5e935e1a5..23f4ef7dd 100644 --- a/packages/fig/tsdown.config.ts +++ b/packages/fig/tsdown.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ entry: { index: './src/index.ts', + 'instance-overrides': './src/instance-overrides/index.ts', 'node-change': './src/node-change/index.ts' }, platform: 'neutral', diff --git a/packages/pen/src/parse-path.ts b/packages/pen/src/parse-path.ts deleted file mode 100644 index 63f9a2ce1..000000000 --- a/packages/pen/src/parse-path.ts +++ /dev/null @@ -1 +0,0 @@ -export { parseSVGPath } from '@open-pencil/scene-graph/parse-path' diff --git a/packages/pen/src/read.ts b/packages/pen/src/read.ts index f77fb005c..814eb38b2 100644 --- a/packages/pen/src/read.ts +++ b/packages/pen/src/read.ts @@ -2,6 +2,7 @@ import { SceneGraph } from '@open-pencil/scene-graph' import type { LayoutMode, LayoutSizing, SceneNode, VectorNetwork } from '@open-pencil/scene-graph' import { copyEffects, copyFills, copyStrokes } from '@open-pencil/scene-graph/copy' import { populateInstanceChildren } from '@open-pencil/scene-graph/instances' +import { parseSVGPath } from '@open-pencil/scene-graph/parse-path' import { applyCornerRadius, @@ -24,7 +25,6 @@ import { type PenNode, type VarContext } from './convert' -import { parseSVGPath } from './parse-path' function scaleVectorNetwork(vn: VectorNetwork, targetW: number, targetH: number): void { if (vn.vertices.length === 0) return diff --git a/tests/engine/io/fig/import/boolean-operation.test.ts b/tests/engine/io/fig/import/boolean-operation.test.ts index a7797cab3..d2df2850e 100644 --- a/tests/engine/io/fig/import/boolean-operation.test.ts +++ b/tests/engine/io/fig/import/boolean-operation.test.ts @@ -1,9 +1,9 @@ import { describe, expect, test } from 'bun:test' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' import { importNodeChanges } from '#core/kiwi/fig/import' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' describe('Figma boolean operation import', () => { test('preserves boolean operation nodes', () => { diff --git a/tests/engine/io/fig/import/derived-symbol-data.test.ts b/tests/engine/io/fig/import/derived-symbol-data.test.ts index 6633369ce..eff86f7ac 100644 --- a/tests/engine/io/fig/import/derived-symbol-data.test.ts +++ b/tests/engine/io/fig/import/derived-symbol-data.test.ts @@ -1,11 +1,12 @@ import { describe, expect, test } from 'bun:test' +import { + buildDsdLayoutUpdates, + propagateDsdChanges, + type OverrideContext +} from '@open-pencil/fig/instance-overrides' import { SceneGraph } from '@open-pencil/scene-graph' -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides' -import { buildDsdLayoutUpdates } from '#core/kiwi/fig/instance-overrides/derived-symbol-data/layout' -import { propagateDsdChanges } from '#core/kiwi/fig/instance-overrides/derived-symbol-data/propagate' - function pageId(graph: SceneGraph): string { return graph.getPages()[0].id } diff --git a/tests/engine/io/fig/import/field-protection.test.ts b/tests/engine/io/fig/import/field-protection.test.ts index b9de4844c..a4682dd75 100644 --- a/tests/engine/io/fig/import/field-protection.test.ts +++ b/tests/engine/io/fig/import/field-protection.test.ts @@ -1,11 +1,13 @@ import { describe, expect, test } from 'bun:test' +import { + protectField, + syncNodeProps, + type ProtectionMap +} from '@open-pencil/fig/instance-overrides' import { SceneGraph } from '@open-pencil/scene-graph' import type { Fill, Stroke } from '@open-pencil/scene-graph' -import { protectField, type ProtectionMap } from '#core/kiwi/fig/instance-overrides/patches' -import { syncNodeProps } from '#core/kiwi/fig/instance-overrides/sync' - function pageId(graph: SceneGraph): string { return graph.getPages()[0].id } diff --git a/tests/engine/io/fig/import/font-variations.test.ts b/tests/engine/io/fig/import/font-variations.test.ts index fd1c311da..c633f0ff4 100644 --- a/tests/engine/io/fig/import/font-variations.test.ts +++ b/tests/engine/io/fig/import/font-variations.test.ts @@ -1,9 +1,8 @@ import { describe, expect, test } from 'bun:test' +import { nodeChangeToProps, importStyleRuns } from '@open-pencil/fig/node-change' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' -import { nodeChangeToProps, importStyleRuns } from '#core/kiwi/fig/node-change/convert' - describe('Figma font variation import', () => { test('imports base text variable font axes', () => { const props = nodeChangeToProps( diff --git a/tests/engine/io/fig/import/group-reclassify.test.ts b/tests/engine/io/fig/import/group-reclassify.test.ts index 1ad6f3927..b641eedb6 100644 --- a/tests/engine/io/fig/import/group-reclassify.test.ts +++ b/tests/engine/io/fig/import/group-reclassify.test.ts @@ -1,7 +1,8 @@ import { describe, expect, test } from 'bun:test' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' + import type { NodeChange } from '#core/kiwi/fig/codec' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' import { parseFixture } from '#tests/helpers/fig-fixtures' import { collectAllNodes } from '#tests/helpers/fig-traversal' diff --git a/tests/engine/io/fig/import/mask-oracle.test.ts b/tests/engine/io/fig/import/mask-oracle.test.ts index 6ee1f7df3..0b7003a65 100644 --- a/tests/engine/io/fig/import/mask-oracle.test.ts +++ b/tests/engine/io/fig/import/mask-oracle.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from 'bun:test' import { readFileSync } from 'node:fs' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' import { SceneGraph, type MaskType } from '@open-pencil/scene-graph' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' import { sceneNodeToKiwi } from '#core/kiwi/fig/node-change/serialize' interface MaskOracleEntry { diff --git a/tests/engine/io/fig/import/masks.test.ts b/tests/engine/io/fig/import/masks.test.ts index d71e7981a..b3a259714 100644 --- a/tests/engine/io/fig/import/masks.test.ts +++ b/tests/engine/io/fig/import/masks.test.ts @@ -1,9 +1,8 @@ import { describe, expect, test } from 'bun:test' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' - describe('Figma mask import', () => { test('imports schema mask fields', () => { const props = nodeChangeToProps( diff --git a/tests/engine/io/fig/import/paint-schema-fields.test.ts b/tests/engine/io/fig/import/paint-schema-fields.test.ts index 783d58d50..0cee28191 100644 --- a/tests/engine/io/fig/import/paint-schema-fields.test.ts +++ b/tests/engine/io/fig/import/paint-schema-fields.test.ts @@ -1,9 +1,8 @@ import { describe, expect, test } from 'bun:test' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' import type { NodeChange } from '@open-pencil/kiwi/fig/codec' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' - const sourceNodeId = { sessionID: 12, localID: 34 } const customEffectId = { guid: { sessionID: 56, localID: 78 } } diff --git a/tests/engine/io/fig/import/raw-field-coverage.test.ts b/tests/engine/io/fig/import/raw-field-coverage.test.ts index a9a75844d..7c2c42a4c 100644 --- a/tests/engine/io/fig/import/raw-field-coverage.test.ts +++ b/tests/engine/io/fig/import/raw-field-coverage.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'bun:test' -import { FIGMA_RAW_NODE_FIELD_KEYS } from '#core/kiwi/fig/node-change/convert' +import { FIGMA_RAW_NODE_FIELD_KEYS } from '@open-pencil/fig/node-change' const RAW_FIELD_COVERAGE = { rendered: [ diff --git a/tests/engine/io/fig/import/rich-text-oracle.test.ts b/tests/engine/io/fig/import/rich-text-oracle.test.ts index 1715c3f70..77d577766 100644 --- a/tests/engine/io/fig/import/rich-text-oracle.test.ts +++ b/tests/engine/io/fig/import/rich-text-oracle.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from 'bun:test' import { readFileSync } from 'node:fs' +import { nodeChangeToProps } from '@open-pencil/fig/node-change' import type { NodeChange, Paint } from '@open-pencil/kiwi/fig/codec' import { SceneGraph } from '@open-pencil/scene-graph' -import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert' import { sceneNodeToKiwi } from '#core/kiwi/fig/node-change/serialize' interface OracleColor { diff --git a/tests/engine/io/fig/import/schema-coverage.test.ts b/tests/engine/io/fig/import/schema-coverage.test.ts index 9b1ffbb87..fbefc34b7 100644 --- a/tests/engine/io/fig/import/schema-coverage.test.ts +++ b/tests/engine/io/fig/import/schema-coverage.test.ts @@ -3,10 +3,9 @@ import { readFileSync } from 'node:fs' import ts from 'typescript' +import { FIGMA_RAW_NODE_FIELD_KEYS } from '@open-pencil/fig/node-change' import { parseSchema } from '@open-pencil/kiwi/schema-runtime' -import { FIGMA_RAW_NODE_FIELD_KEYS } from '#core/kiwi/fig/node-change/convert' - interface SchemaField { name: string type: string | null diff --git a/tests/engine/io/fig/roundtrip/source-metadata.test.ts b/tests/engine/io/fig/roundtrip/source-metadata.test.ts index 0456fffd5..b911015a5 100644 --- a/tests/engine/io/fig/roundtrip/source-metadata.test.ts +++ b/tests/engine/io/fig/roundtrip/source-metadata.test.ts @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, test } from 'bun:test' import { exportFigFile, initCodec, parseFigFile, SceneGraph } from '@open-pencil/core' -import { guidToString } from '@open-pencil/core/kiwi/fig/node-change/convert' import { parseFigBuffer } from '@open-pencil/fig' +import { guidToString } from '@open-pencil/fig/node-change' function decodeExport(bytes: Uint8Array) { return parseFigBuffer(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)) diff --git a/tests/engine/kiwi/instance-overrides/sync-clones.test.ts b/tests/engine/kiwi/instance-overrides/sync-clones.test.ts index d43bb26d3..f2dbfe677 100644 --- a/tests/engine/kiwi/instance-overrides/sync-clones.test.ts +++ b/tests/engine/kiwi/instance-overrides/sync-clones.test.ts @@ -1,8 +1,7 @@ import { describe, expect, test } from 'bun:test' import { SceneGraph } from '@open-pencil/core' - -import { syncChildrenDeep } from '#core/kiwi/fig/instance-overrides/sync/clones' +import { syncChildrenDeep } from '@open-pencil/fig/instance-overrides' describe('instance override clone sync', () => { test('reclones nested instance children when the source component changes', () => { diff --git a/tests/engine/pen/var-padding.test.ts b/tests/engine/pen/var-padding.test.ts index 5495d37f0..df412835c 100644 --- a/tests/engine/pen/var-padding.test.ts +++ b/tests/engine/pen/var-padding.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'bun:test' import type { SceneNode } from '@open-pencil/core' -import { applyPadding, isVarRef, type VarContext } from '@open-pencil/core/io/formats/pen' +import { applyPadding, isVarRef, type VarContext } from '@open-pencil/pen' /** * Regression test for open-pencil/open-pencil#201 diff --git a/tests/engine/render/canvas/visual/boolean-matrix.ts b/tests/engine/render/canvas/visual/boolean-matrix.ts index 7b36c8b51..65ed5ca36 100644 --- a/tests/engine/render/canvas/visual/boolean-matrix.ts +++ b/tests/engine/render/canvas/visual/boolean-matrix.ts @@ -1,10 +1,10 @@ import { writeFile } from 'node:fs/promises' import type { SceneNode } from '@open-pencil/scene-graph' +import { parseSVGPath } from '@open-pencil/scene-graph/parse-path' import { initCanvasKit } from '#cli/headless' import { SkiaRenderer } from '#core/canvas' -import { parseSVGPath } from '#core/io/formats/svg/parse-path' import { createAPI } from '#tests/engine/figma/api/helpers' import { expectDefined } from '#tests/helpers/assert' diff --git a/tools/architecture/src/steiger-rules/cross-package-reexport-shims.ts b/tools/architecture/src/steiger-rules/cross-package-reexport-shims.ts new file mode 100644 index 000000000..3adc88887 --- /dev/null +++ b/tools/architecture/src/steiger-rules/cross-package-reexport-shims.ts @@ -0,0 +1,28 @@ +import ts from 'typescript' + +import { createTextRule, type Rule } from './support.ts' + +export function isCrossPackageReexportShim(sourceRel: string, content: string): boolean { + if (!/^packages\/[^/]+\/src\/.*\.[cm]?tsx?$/u.test(sourceRel)) return false + const source = ts.createSourceFile(sourceRel, content, ts.ScriptTarget.Latest, false) + if (source.statements.length === 0) return false + return source.statements.every( + (statement) => + ts.isExportDeclaration(statement) && + ts.isStringLiteral(statement.moduleSpecifier) && + statement.moduleSpecifier.text.startsWith('@open-pencil/') + ) +} + +export const noCrossPackageReexportShims: Rule = createTextRule( + 'open-pencil/no-cross-package-reexport-shims', + (sourceRel, content) => + isCrossPackageReexportShim(sourceRel, content) + ? [ + { + message: + 'Remove this cross-package re-export shim and import the owning package directly.' + } + ] + : [] +) diff --git a/tools/architecture/src/steiger-rules/index.ts b/tools/architecture/src/steiger-rules/index.ts index 7e8d5a8e6..956bb0697 100644 --- a/tools/architecture/src/steiger-rules/index.ts +++ b/tools/architecture/src/steiger-rules/index.ts @@ -2,6 +2,7 @@ import path from 'node:path' import { parse as parseVueSfc } from 'vue/compiler-sfc' +import { noCrossPackageReexportShims } from './cross-package-reexport-shims.ts' import { noDynamicTailwindStateClasses, noVueTemplateUiHooksOrSvg @@ -496,6 +497,7 @@ export const openPencilArchitecturePlugin = { meta: { name: 'open-pencil-architecture', version: '0.0.0' }, ruleDefinitions: [ preferDomainFoldersOverFilenamePrefixes, + noCrossPackageReexportShims, scriptsAreEntrypointShims, strictToolsLayout, strictTestFilePlacement, diff --git a/tools/architecture/tests/cross-package-reexports.test.ts b/tools/architecture/tests/cross-package-reexports.test.ts new file mode 100644 index 000000000..911995de5 --- /dev/null +++ b/tools/architecture/tests/cross-package-reexports.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, test } from 'bun:test' + +import { isCrossPackageReexportShim } from '../src/steiger-rules/cross-package-reexport-shims' + +describe('cross-package re-export shims', () => { + test('rejects star and named forwarding-only modules', () => { + expect( + isCrossPackageReexportShim( + 'packages/core/src/kiwi/fig/instance-overrides/index.ts', + "export * from '@open-pencil/fig/instance-overrides'\n" + ) + ).toBe(true) + expect( + isCrossPackageReexportShim( + 'packages/core/src/io/formats/fig/compress.ts', + "export { compressFigDataSync } from '@open-pencil/fig'\n" + ) + ).toBe(true) + }) + + test('allows package barrels with owned exports or implementation', () => { + expect( + isCrossPackageReexportShim( + 'packages/core/src/index.ts', + "export { parsePenFile } from '@open-pencil/pen'\nexport { createEditor } from './editor'\n" + ) + ).toBe(false) + expect( + isCrossPackageReexportShim( + 'packages/core/src/io/formats.ts', + "import { parsePenFile } from '@open-pencil/pen'\nexport const pen = parsePenFile\n" + ) + ).toBe(false) + }) +}) diff --git a/tools/package-quality/src/smoke.ts b/tools/package-quality/src/smoke.ts index c453e73f8..8998dd9a0 100644 --- a/tools/package-quality/src/smoke.ts +++ b/tools/package-quality/src/smoke.ts @@ -97,6 +97,10 @@ try { "const { convertLineHeight } = await import('@open-pencil/fig/node-change'); if (convertLineHeight({ value: 120, units: 'PERCENT' }, 20) !== 24) throw new Error('Fig NodeChange subpath failed')", tempDir ) + nodeEval( + "const { populateAndApplyOverrides } = await import('@open-pencil/fig/instance-overrides'); if (typeof populateAndApplyOverrides !== 'function') throw new Error('Fig instance override subpath failed')", + tempDir + ) nodeEval( "const { SceneGraph } = await import('@open-pencil/scene-graph'); const graph = new SceneGraph(); if (graph.getPages().length !== 1) throw new Error('SceneGraph package smoke failed')", tempDir