@wp-typia/project-tools 0.22.2 → 0.22.4
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/dist/runtime/built-in-block-code-templates/interactivity.d.ts +1 -1
- package/dist/runtime/built-in-block-code-templates/interactivity.js +4 -2
- package/dist/runtime/cli-add-block-json.d.ts +31 -0
- package/dist/runtime/cli-add-block-json.js +65 -0
- package/dist/runtime/cli-add-collision.d.ts +129 -0
- package/dist/runtime/cli-add-collision.js +293 -0
- package/dist/runtime/cli-add-filesystem.d.ts +29 -0
- package/dist/runtime/cli-add-filesystem.js +77 -0
- package/dist/runtime/cli-add-help.d.ts +4 -0
- package/dist/runtime/cli-add-help.js +41 -0
- package/dist/runtime/cli-add-shared.d.ts +6 -255
- package/dist/runtime/cli-add-shared.js +6 -391
- package/dist/runtime/cli-add-types.d.ts +247 -0
- package/dist/runtime/cli-add-types.js +64 -0
- package/dist/runtime/cli-add-validation.d.ts +87 -0
- package/dist/runtime/cli-add-validation.js +147 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +5 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +366 -0
- package/dist/runtime/cli-add-workspace-ability-templates.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-ability-templates.js +500 -0
- package/dist/runtime/cli-add-workspace-ability-types.d.ts +27 -0
- package/dist/runtime/cli-add-workspace-ability-types.js +14 -0
- package/dist/runtime/cli-add-workspace-ability.js +12 -852
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +35 -61
- package/dist/runtime/cli-add-workspace-ai-scaffold.d.ts +21 -0
- package/dist/runtime/cli-add-workspace-ai-scaffold.js +87 -0
- package/dist/runtime/cli-add-workspace-ai-templates.d.ts +4 -0
- package/dist/runtime/cli-add-workspace-ai-templates.js +607 -0
- package/dist/runtime/cli-add-workspace-ai.js +15 -688
- package/dist/runtime/cli-add-workspace-assets.js +7 -4
- package/dist/runtime/cli-add-workspace-mutation.d.ts +30 -0
- package/dist/runtime/cli-add-workspace-mutation.js +60 -0
- package/dist/runtime/cli-add-workspace.js +2 -98
- package/dist/runtime/cli-add.d.ts +2 -2
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-doctor-workspace-bindings.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-bindings.js +134 -0
- package/dist/runtime/cli-doctor-workspace-blocks.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-blocks.js +439 -0
- package/dist/runtime/cli-doctor-workspace-features.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features.js +383 -0
- package/dist/runtime/cli-doctor-workspace-package.d.ts +18 -0
- package/dist/runtime/cli-doctor-workspace-package.js +59 -0
- package/dist/runtime/cli-doctor-workspace-shared.d.ts +69 -0
- package/dist/runtime/cli-doctor-workspace-shared.js +87 -0
- package/dist/runtime/cli-doctor-workspace.js +25 -1062
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/migration-utils.d.ts +2 -1
- package/dist/runtime/migration-utils.js +3 -11
- package/dist/runtime/package-managers.d.ts +19 -0
- package/dist/runtime/package-managers.js +62 -0
- package/dist/runtime/template-source-cache.d.ts +59 -0
- package/dist/runtime/template-source-cache.js +160 -0
- package/dist/runtime/ts-source-masking.d.ts +28 -0
- package/dist/runtime/ts-source-masking.js +104 -0
- package/dist/runtime/typia-llm.d.ts +9 -1
- package/dist/runtime/typia-llm.js +20 -5
- package/dist/runtime/workspace-inventory.js +116 -59
- package/dist/runtime/workspace-project.d.ts +1 -1
- package/dist/runtime/workspace-project.js +2 -10
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const INTERACTIVITY_EDIT_TEMPLATE = "import type { BlockEditProps } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport { useBlockProps, InspectorControls, RichText, BlockControls, AlignmentToolbar } from '@wordpress/block-editor';\nimport { PanelBody, RangeControl, Button, Notice } from '@wordpress/components';\nimport { useState } from '@wordpress/element';\nimport currentManifest from './manifest-document';\nimport { {{slugCamelCase}}Store } from './interactivity-store';\nimport {\n InspectorFromManifest,\n useEditorFields,\n useTypedAttributeUpdater,\n} from '@wp-typia/block-runtime/inspector';\nimport type { {{pascalCase}}Attributes } from './types';\nimport {\n sanitize{{pascalCase}}Attributes,\n validate{{pascalCase}}Attributes,\n} from './validators';\nimport { useTypiaValidation } from './hooks';\n\ntype EditProps = BlockEditProps<{{pascalCase}}Attributes>;\n\nconst actionButtonRowStyle = { display: 'flex', gap: '8px', marginTop: '16px' };\nconst validationListStyle = { margin: 0, paddingLeft: '1em' };\n\nexport default function Edit({ attributes, setAttributes, isSelected }: EditProps) {\n const [isPreviewing, setIsPreviewing] = useState(false);\n const editorFields = useEditorFields(currentManifest, {\n manual: ['content', 'clickCount', 'maxClicks'],\n labels: {\n alignment: __('Alignment', '{{textDomain}}'),\n animation: __('Animation', '{{textDomain}}'),\n interactiveMode: __('Interactive Mode', '{{textDomain}}'),\n isVisible: __('Visible', '{{textDomain}}'),\n showCounter: __('Show Counter', '{{textDomain}}'),\n },\n });\n const { errorMessages, isValid } = useTypiaValidation(\n attributes,\n validate{{pascalCase}}Attributes,\n );\n const validateEditorUpdate = (nextAttributes: {{pascalCase}}Attributes) => {\n try {\n return {\n data: sanitize{{pascalCase}}Attributes(nextAttributes),\n errors: [],\n isValid: true as const,\n };\n } catch {\n return validate{{pascalCase}}Attributes(nextAttributes);\n }\n };\n const { updateField } = useTypedAttributeUpdater(\n attributes,\n setAttributes,\n validateEditorUpdate\n );\n const alignmentValue = editorFields.getStringValue(\n attributes,\n 'alignment',\n 'left'\n ) as NonNullable<{{pascalCase}}Attributes['alignment']>;\n const clickCount = attributes.clickCount ?? 0;\n const isVisible = editorFields.getBooleanValue(\n attributes,\n 'isVisible',\n true\n );\n const isAnimating = attributes.isAnimating ?? false;\n const maxClicks = attributes.maxClicks ?? 0;\n const showCounter = editorFields.getBooleanValue(\n attributes,\n 'showCounter',\n true\n );\n const interactiveMode = editorFields.getStringValue(\n attributes,\n 'interactiveMode',\n 'click'\n ) as NonNullable<{{pascalCase}}Attributes['interactiveMode']>;\n const animation = editorFields.getStringValue(\n attributes,\n 'animation',\n 'none'\n ) as NonNullable<{{pascalCase}}Attributes['animation']>;\n\n const blockProps = useBlockProps({\n className: `{{cssClassName}} {{cssClassName}}--${interactiveMode}`,\n 'data-wp-interactive': {{slugCamelCase}}Store.directive.interactive,\n 'data-wp-context': JSON.stringify(\n {{slugCamelCase}}Store.createContext({\n clicks: clickCount,\n isAnimating,\n isVisible,\n animation,\n maxClicks,\n })\n )\n });\n const previewContentStyle = { textAlign: alignmentValue };\n const progressBarStyle = { width: `${(clickCount / maxClicks) * 100}%` };\n const clicksDirective = {{slugCamelCase}}Store.directive.state('clicks');\n const isAnimatingDirective = {{slugCamelCase}}Store.directive.state('isAnimating');\n const progressDirective = {{slugCamelCase}}Store.directive.state('progress') + \" + '%'\";\n\n const resetCounter = () => {\n updateField('clickCount', 0);\n updateField('isAnimating', false);\n };\n\n const testAnimation = () => {\n updateField('isAnimating', true);\n setTimeout(() => {\n updateField('isAnimating', false);\n }, 1000);\n };\n\n return (\n <>\n <BlockControls>\n <AlignmentToolbar\n value={alignmentValue}\n onChange={(value) => updateField('alignment', (value || alignmentValue) as NonNullable<{{pascalCase}}Attributes['alignment']>)}\n />\n </BlockControls>\n\n <InspectorControls>\n <InspectorFromManifest\n attributes={attributes}\n fieldLookup={editorFields}\n onChange={updateField}\n paths={['alignment', 'interactiveMode', 'animation', 'showCounter', 'isVisible']}\n title={__('Interactive Settings', '{{textDomain}}')}\n />\n\n <PanelBody title={__('Counter Settings', '{{textDomain}}')}>\n <RangeControl\n label={__('Max Clicks', '{{textDomain}}')}\n value={maxClicks}\n onChange={(value) => updateField('maxClicks', value ?? maxClicks)}\n min={0}\n max={100}\n help={__('Set to 0 for unlimited clicks', '{{textDomain}}')}\n />\n\n <div style={actionButtonRowStyle}>\n <Button\n variant=\"secondary\"\n onClick={resetCounter}\n isSmall\n >\n {__('Reset Counter', '{{textDomain}}')}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={testAnimation}\n isSmall\n >\n {__('Test Animation', '{{textDomain}}')}\n </Button>\n </div>\n </PanelBody>\n\n {!isValid && (\n <PanelBody title={__('Validation Errors', '{{textDomain}}')} initialOpen>\n {errorMessages.map((error, index) => (\n <Notice key={index} status=\"error\" isDismissible={false}>\n {error}\n </Notice>\n ))}\n </PanelBody>\n )}\n\n {isSelected && (\n <PanelBody title={__('Preview', '{{textDomain}}')}>\n <Button\n variant={isPreviewing ? 'primary' : 'secondary'}\n onClick={() => setIsPreviewing(!isPreviewing)}\n aria-pressed={isPreviewing}\n isSmall\n >\n {isPreviewing\n ? __('Disable Preview Mode', '{{textDomain}}')\n : __('Enable Preview Mode', '{{textDomain}}')}\n </Button>\n\n {clickCount > 0 && (\n <Notice status=\"info\" isDismissible={false}>\n {__('Current clicks:', '{{textDomain}}')} {clickCount}\n {maxClicks > 0 && ` / ${maxClicks}`}\n </Notice>\n )}\n </PanelBody>\n )}\n </InspectorControls>\n\n <div {...blockProps}>\n <div\n className={`{{cssClassName}}__content ${isAnimating ? 'is-animating' : ''}`}\n style={previewContentStyle}\n data-wp-on--click={isPreviewing ? {{slugCamelCase}}Store.directive.action('handleClick') : undefined}\n data-wp-on--mouseenter={isPreviewing && interactiveMode === 'hover' ? {{slugCamelCase}}Store.directive.action('handleMouseEnter') : undefined}\n data-wp-on--mouseleave={isPreviewing && interactiveMode === 'hover' ? {{slugCamelCase}}Store.directive.action('handleMouseLeave') : undefined}\n >\n <RichText\n tagName=\"p\"\n value={attributes.content}\n onChange={(value) => updateField('content', value)}\n placeholder={__( {{titleJson}} + ' \u2013 click me to interact!', '{{textDomain}}')}\n />\n\n {!isValid && (\n <Notice status=\"error\" isDismissible={false}>\n <p>\n <strong>{__('Validation Errors', '{{textDomain}}')}</strong>\n </p>\n <ul style={validationListStyle}>\n {errorMessages.map((error, index) => (\n <li key={index}>{error}</li>\n ))}\n </ul>\n </Notice>\n )}\n\n {showCounter && (\n <div className=\"{{cssClassName}}__counter\">\n <span className=\"{{cssClassName}}__counter-label\">\n {__('Clicks:', '{{textDomain}}')}\n </span>\n <span\n className=\"{{cssClassName}}__counter-value\"\n data-wp-text={clicksDirective}\n >\n {clickCount}\n </span>\n </div>\n )}\n\n {maxClicks > 0 && (\n <div className=\"{{cssClassName}}__progress\">\n <div\n className=\"{{cssClassName}}__progress-bar\"\n style={progressBarStyle}\n data-wp-style--width={progressDirective}\n />\n </div>\n )}\n\n {animation !== 'none' && (\n <div\n className={`{{cssClassName}}__animation ${isAnimating ? 'is-active' : ''}`}\n data-wp-class--is-active={isAnimatingDirective}\n >\n {animation}\n </div>\n )}\n </div>\n </div>\n </>\n );\n}\n";
|
|
2
2
|
export declare const INTERACTIVITY_SAVE_TEMPLATE = "import { useBlockProps, RichText } from '@wordpress/block-editor';\nimport { __ } from '@wordpress/i18n';\nimport { {{slugCamelCase}}Store } from './interactivity-store';\nimport type { {{pascalCase}}Attributes } from './types';\n\nexport default function Save({ attributes }: { attributes: {{pascalCase}}Attributes }) {\n const clickCount = attributes.clickCount ?? 0;\n const interactiveMode = attributes.interactiveMode ?? 'click';\n const animation = attributes.animation ?? 'none';\n const isAnimating = attributes.isAnimating ?? false;\n const isVisible = attributes.isVisible ?? true;\n const maxClicks = attributes.maxClicks ?? 0;\n const showCounter = attributes.showCounter ?? true;\n const contentStyle = { textAlign: attributes.alignment };\n const clickActionDirective = {{slugCamelCase}}Store.directive.action('handleClick');\n const visibilityHiddenDirective = {{slugCamelCase}}Store.directive.negate(\n {{slugCamelCase}}Store.directive.state('isVisible')\n );\n const clicksDirective = {{slugCamelCase}}Store.directive.state('clicks');\n const clampedClicksDirective = {{slugCamelCase}}Store.directive.state('clampedClicks');\n const isAnimatingDirective = {{slugCamelCase}}Store.directive.state('isAnimating');\n const completionHiddenDirective = {{slugCamelCase}}Store.directive.negate(\n {{slugCamelCase}}Store.directive.state('isComplete')\n );\n const resetActionDirective = {{slugCamelCase}}Store.directive.action('reset');\n const blockProps = useBlockProps.save({\n className: `{{cssClassName}} {{cssClassName}}--${interactiveMode}`,\n 'data-wp-interactive': {{slugCamelCase}}Store.directive.interactive,\n 'data-wp-context': JSON.stringify(\n {{slugCamelCase}}Store.createContext({\n clicks: clickCount,\n isAnimating,\n isVisible,\n animation,\n maxClicks,\n })\n )\n });\n const progressDirective = {{slugCamelCase}}Store.directive.state('progress') + \" + '%'\";\n\n return (\n <div {...blockProps}>\n <div\n className={`{{cssClassName}}__content ${isAnimating ? 'is-animating' : ''}`}\n style={contentStyle}\n data-wp-on--click={clickActionDirective}\n data-wp-on--mouseenter={interactiveMode === 'hover' ? {{slugCamelCase}}Store.directive.action('handleMouseEnter') : undefined}\n data-wp-on--mouseleave={interactiveMode === 'hover' ? {{slugCamelCase}}Store.directive.action('handleMouseLeave') : undefined}\n data-wp-bind--hidden={visibilityHiddenDirective}\n >\n <RichText.Content\n tagName=\"p\"\n value={attributes.content}\n className=\"{{cssClassName}}__text\"\n />\n\n {showCounter && (\n <div\n className=\"{{cssClassName}}__counter\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n <span className=\"{{cssClassName}}__counter-label\">\n { __( 'Clicks:', '{{textDomain}}' ) }\n </span>\n <span\n className=\"{{cssClassName}}__counter-value\"\n data-wp-text={clicksDirective}\n >\n {clickCount}\n </span>\n </div>\n )}\n\n {maxClicks > 0 && (\n <div className=\"{{cssClassName}}__progress\">\n <div\n className=\"{{cssClassName}}__progress-bar\"\n role=\"progressbar\"\n aria-label={ __( 'Click progress', '{{textDomain}}' ) }\n aria-valuemin={0}\n aria-valuemax={maxClicks}\n aria-valuenow={Math.min(clickCount, maxClicks)}\n data-wp-bind--aria-valuenow={clampedClicksDirective}\n data-wp-style--width={progressDirective}\n />\n </div>\n )}\n\n <div\n className={`{{cssClassName}}__animation ${animation}`}\n aria-hidden=\"true\"\n data-wp-class--is-active={isAnimatingDirective}\n />\n\n {maxClicks > 0 && (\n <div\n className=\"{{cssClassName}}__completion\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-wp-bind--hidden={completionHiddenDirective}\n >\n { __( '\uD83C\uDF89 Complete!', '{{textDomain}}' ) }\n </div>\n )}\n\n <button\n className=\"{{cssClassName}}__reset\"\n data-wp-on--click={resetActionDirective}\n aria-label={ __( 'Reset counter', '{{textDomain}}' ) }\n >\n <span aria-hidden=\"true\">\u21BB</span>\n <span className=\"screen-reader-text\">\n { __( 'Reset counter', '{{textDomain}}' ) }\n </span>\n </button>\n </div>\n </div>\n );\n}\n";
|
|
3
3
|
export declare const INTERACTIVITY_INDEX_TEMPLATE = "import {\n registerScaffoldBlockType,\n type BlockConfiguration,\n} from '@wp-typia/block-types/blocks/registration';\nimport type { BlockSupports } from '@wp-typia/block-types/blocks/supports';\nimport {\n buildScaffoldBlockRegistration,\n parseScaffoldBlockMetadata,\n} from '@wp-typia/block-runtime/blocks';\n\nimport Edit from './edit';\nimport Save from './save';\nimport metadata from './block-metadata';\nimport './editor.scss';\nimport './style.scss';\n\nimport type { {{pascalCase}}Attributes } from './types';\n\nconst scaffoldSupports = {\n html: false,\n align: true,\n anchor: true,\n className: true,\n interactivity: true,\n} satisfies BlockSupports;\n\nconst registration = buildScaffoldBlockRegistration(\n parseScaffoldBlockMetadata<BlockConfiguration<{{pascalCase}}Attributes>>(metadata),\n {\n supports: scaffoldSupports,\n edit: Edit,\n save: Save,\n }\n);\n\nregisterScaffoldBlockType(registration.name, registration.settings);\n";
|
|
4
|
-
export declare const INTERACTIVITY_STORE_TEMPLATE = "import type {\n {{pascalCase}}Context,\n {{pascalCase}}State,\n} from './types';\n\ntype InteractivityActionShape = object;\ntype InteractivityCallbackShape = object;\ntype InteractivityContextShape = object;\ntype InteractivityStateShape = object;\ntype InteractivityCallable
|
|
4
|
+
export declare const INTERACTIVITY_STORE_TEMPLATE = "import type {\n {{pascalCase}}Context,\n {{pascalCase}}State,\n} from './types';\n\ntype InteractivityActionShape = object;\ntype InteractivityCallbackShape = object;\ntype InteractivityContextShape = object;\ntype InteractivityStateShape = object;\ntype InteractivityCallable =\n | ((...args: unknown[]) => unknown)\n | ReturnType<typeof import('@wordpress/interactivity').withSyncEvent>;\ntype InteractivityKey<T extends object> = Extract<keyof T, string>;\ntype InteractivityMethodKey<T extends object> = {\n [Key in InteractivityKey<T>]: T[Key] extends InteractivityCallable ? Key : never;\n}[InteractivityKey<T>];\n\ntype InteractivityDirectivePath<\n Root extends string,\n Key extends string,\n> = `${Root}.${Key}`;\n\ntype NegatedInteractivityDirectivePath<Path extends string> = `!${Path}`;\n\nexport interface TypedInteractivityDirectiveHelpers<\n State extends InteractivityStateShape,\n Context extends InteractivityContextShape,\n Actions extends InteractivityActionShape,\n Callbacks extends InteractivityCallbackShape,\n Namespace extends string,\n> {\n readonly interactive: Namespace;\n action<Key extends InteractivityMethodKey<Actions>>(\n key: Key,\n ): InteractivityDirectivePath<'actions', Key>;\n callback<Key extends InteractivityMethodKey<Callbacks>>(\n key: Key,\n ): InteractivityDirectivePath<'callbacks', Key>;\n state<Key extends InteractivityKey<State>>(\n key: Key,\n ): InteractivityDirectivePath<'state', Key>;\n context<Key extends InteractivityKey<Context>>(\n key: Key,\n ): InteractivityDirectivePath<'context', Key>;\n negate<Path extends string>(\n path: Path,\n ): NegatedInteractivityDirectivePath<Path>;\n}\n\nexport interface TypedInteractivityStore<\n Namespace extends string,\n State extends InteractivityStateShape,\n Context extends InteractivityContextShape,\n Actions extends InteractivityActionShape,\n Callbacks extends InteractivityCallbackShape,\n> {\n readonly namespace: Namespace;\n readonly state: State;\n readonly context: Context;\n readonly actions: Actions;\n readonly callbacks: Callbacks;\n readonly directive: TypedInteractivityDirectiveHelpers<\n State,\n Context,\n Actions,\n Callbacks,\n Namespace\n >;\n createContext(value: Context): Context;\n}\n\nexport function defineInteractivityStore<\n Namespace extends string,\n State extends InteractivityStateShape,\n Context extends InteractivityContextShape,\n Actions extends InteractivityActionShape,\n Callbacks extends InteractivityCallbackShape,\n>(config: {\n readonly namespace: Namespace;\n readonly state: State;\n readonly context: Context;\n readonly actions: Actions;\n readonly callbacks: Callbacks;\n}): TypedInteractivityStore<Namespace, State, Context, Actions, Callbacks> {\n return {\n namespace: config.namespace,\n state: config.state,\n context: config.context,\n actions: config.actions,\n callbacks: config.callbacks,\n directive: {\n interactive: config.namespace,\n action<Key extends InteractivityMethodKey<Actions>>(key: Key) {\n return `actions.${key}` as InteractivityDirectivePath<'actions', Key>;\n },\n callback<Key extends InteractivityMethodKey<Callbacks>>(key: Key) {\n return `callbacks.${key}` as InteractivityDirectivePath<'callbacks', Key>;\n },\n state<Key extends InteractivityKey<State>>(key: Key) {\n return `state.${key}` as InteractivityDirectivePath<'state', Key>;\n },\n context<Key extends InteractivityKey<Context>>(key: Key) {\n return `context.${key}` as InteractivityDirectivePath<'context', Key>;\n },\n negate<Path extends string>(path: Path) {\n return `!${path}` as NegatedInteractivityDirectivePath<Path>;\n },\n },\n createContext(value) {\n return value;\n },\n };\n}\n\ntype InteractivityActionHandler = InteractivityCallable;\n\nexport interface {{pascalCase}}StoreActions {\n handleClick: InteractivityActionHandler;\n handleMouseEnter: InteractivityActionHandler;\n handleMouseLeave: InteractivityActionHandler;\n reset: InteractivityActionHandler;\n}\n\nexport interface {{pascalCase}}StoreCallbacks {}\n\nexport const {{slugCamelCase}}Store = defineInteractivityStore({\n namespace: '{{slugKebabCase}}',\n state: {} as {{pascalCase}}State,\n context: {} as {{pascalCase}}Context,\n actions: {} as {{pascalCase}}StoreActions,\n callbacks: {} as {{pascalCase}}StoreCallbacks,\n});\n";
|
|
5
5
|
export declare const INTERACTIVITY_SCRIPT_TEMPLATE = "/**\n * WordPress Interactivity API implementation for {{title}} block\n */\nimport { store, getContext, getElement, withSyncEvent } from '@wordpress/interactivity';\nimport {\n {{slugCamelCase}}Store,\n type {{pascalCase}}StoreActions,\n} from './interactivity-store';\nimport type { {{pascalCase}}Context, {{pascalCase}}State } from './types';\n\nfunction getBlockContext() {\n return getContext<{{pascalCase}}Context>();\n}\n\nconst actions: {{pascalCase}}StoreActions = {\n // Handle block click\n handleClick: () => {\n const context = getBlockContext();\n const { ref } = getElement();\n\n if (!ref) {\n return;\n }\n\n if (context.maxClicks > 0 && context.clicks >= context.maxClicks) {\n return;\n }\n\n const previousClicks = context.clicks;\n\n // Increment click counter\n context.clicks += 1;\n\n // Trigger animation\n if (context.animation !== 'none') {\n context.isAnimating = true;\n setTimeout(() => {\n context.isAnimating = false;\n }, 1000);\n }\n\n // Emit custom event\n ref.dispatchEvent(new CustomEvent('{{slugKebabCase}}:click', {\n detail: { clicks: context.clicks }\n }));\n\n // Check if max clicks reached\n if (context.maxClicks > 0 && previousClicks < context.maxClicks && context.clicks === context.maxClicks) {\n ref.dispatchEvent(new CustomEvent('{{slugKebabCase}}:complete', {\n detail: { totalClicks: context.clicks }\n }));\n }\n },\n\n // Handle hover events\n handleMouseEnter: () => {\n const context = getBlockContext();\n if (context.animation === 'none') return;\n context.isAnimating = true;\n },\n\n handleMouseLeave: () => {\n const context = getBlockContext();\n if (context.animation === 'none') return;\n context.isAnimating = false;\n },\n\n // Reset counter\n reset: withSyncEvent((event: Event) => {\n event.stopPropagation();\n const context = getBlockContext();\n context.clicks = 0;\n context.isAnimating = false;\n })\n};\n\nconst state = {\n get clicks() {\n return getBlockContext().clicks;\n },\n get isAnimating() {\n return getBlockContext().isAnimating;\n },\n get isVisible() {\n return getBlockContext().isVisible;\n },\n get progress() {\n const context = getBlockContext();\n const clampedClicks =\n context.maxClicks > 0\n ? Math.min(context.clicks, context.maxClicks)\n : context.clicks;\n return context.maxClicks > 0 ? (clampedClicks / context.maxClicks) * 100 : 0;\n },\n get clampedClicks() {\n const context = getBlockContext();\n return context.maxClicks > 0\n ? Math.min(context.clicks, context.maxClicks)\n : context.clicks;\n },\n get isComplete() {\n const context = getBlockContext();\n return context.clicks >= context.maxClicks && context.maxClicks > 0;\n }\n} satisfies {{pascalCase}}State;\n\n// Store configuration\nstore({{slugCamelCase}}Store.namespace, {\n // State - reactive data that updates the UI\n state,\n actions,\n callbacks: {{slugCamelCase}}Store.callbacks,\n});\n";
|
|
6
6
|
export declare const INTERACTIVITY_VALIDATORS_TEMPLATE = "import typia from 'typia';\nimport currentManifest from \"./manifest-defaults-document\";\nimport { {{pascalCase}}Attributes, {{pascalCase}}ValidationResult } from \"./types\";\nimport { createTemplateValidatorToolkit } from \"./validator-toolkit\";\n\nconst scaffoldValidators = createTemplateValidatorToolkit<{{pascalCase}}Attributes>({\n assert: typia.createAssert<{{pascalCase}}Attributes>(),\n clone: typia.misc.createClone<{{pascalCase}}Attributes>() as (\n value: {{pascalCase}}Attributes,\n ) => {{pascalCase}}Attributes,\n is: typia.createIs<{{pascalCase}}Attributes>(),\n manifest: currentManifest,\n prune: typia.misc.createPrune<{{pascalCase}}Attributes>(),\n random: typia.createRandom<{{pascalCase}}Attributes>() as (\n ...args: unknown[]\n ) => {{pascalCase}}Attributes,\n validate: typia.createValidate<{{pascalCase}}Attributes>(),\n});\n\nexport const validate{{pascalCase}}Attributes =\n scaffoldValidators.validateAttributes as (\n attributes: unknown,\n ) => {{pascalCase}}ValidationResult;\n\nexport const validators = scaffoldValidators.validators;\n\nexport const sanitize{{pascalCase}}Attributes =\n scaffoldValidators.sanitizeAttributes as (\n attributes: Partial<{{pascalCase}}Attributes>,\n ) => {{pascalCase}}Attributes;\n\n/**\n * Runtime type guard for checking if an object is {{pascalCase}}Attributes.\n */\nexport const is{{pascalCase}}Attributes = (obj: unknown): obj is {{pascalCase}}Attributes => {\n return validators.is(obj);\n};\n\nexport const createAttributeUpdater = scaffoldValidators.createAttributeUpdater;\n";
|
|
@@ -428,7 +428,9 @@ type InteractivityActionShape = object;
|
|
|
428
428
|
type InteractivityCallbackShape = object;
|
|
429
429
|
type InteractivityContextShape = object;
|
|
430
430
|
type InteractivityStateShape = object;
|
|
431
|
-
type InteractivityCallable =
|
|
431
|
+
type InteractivityCallable =
|
|
432
|
+
| ((...args: unknown[]) => unknown)
|
|
433
|
+
| ReturnType<typeof import('@wordpress/interactivity').withSyncEvent>;
|
|
432
434
|
type InteractivityKey<T extends object> = Extract<keyof T, string>;
|
|
433
435
|
type InteractivityMethodKey<T extends object> = {
|
|
434
436
|
[Key in InteractivityKey<T>]: T[Key] extends InteractivityCallable ? Key : never;
|
|
@@ -531,7 +533,7 @@ export function defineInteractivityStore<
|
|
|
531
533
|
};
|
|
532
534
|
}
|
|
533
535
|
|
|
534
|
-
type InteractivityActionHandler =
|
|
536
|
+
type InteractivityActionHandler = InteractivityCallable;
|
|
535
537
|
|
|
536
538
|
export interface {{pascalCase}}StoreActions {
|
|
537
539
|
handleClick: InteractivityActionHandler;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve an existing workspace block inventory entry by slug.
|
|
4
|
+
*
|
|
5
|
+
* @param inventory Parsed workspace inventory that owns available blocks.
|
|
6
|
+
* @param blockSlug Workspace block slug to locate.
|
|
7
|
+
* @returns The matching workspace block inventory entry.
|
|
8
|
+
* @throws {Error} When the block slug is not present in the inventory.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveWorkspaceBlock(inventory: WorkspaceInventory, blockSlug: string): WorkspaceInventory["blocks"][number];
|
|
11
|
+
/**
|
|
12
|
+
* Read and validate a workspace block's `block.json` metadata from disk.
|
|
13
|
+
*
|
|
14
|
+
* @param projectDir Absolute workspace root directory.
|
|
15
|
+
* @param blockSlug Workspace block slug whose metadata should be read.
|
|
16
|
+
* @returns Parsed block metadata and the absolute `block.json` path.
|
|
17
|
+
* @throws {Error} When the file is missing or cannot be parsed as scaffold metadata.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readWorkspaceBlockJson(projectDir: string, blockSlug: string): {
|
|
20
|
+
blockJson: Record<string, unknown>;
|
|
21
|
+
blockJsonPath: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Return a mutable `blockHooks` object for a parsed block metadata document.
|
|
25
|
+
*
|
|
26
|
+
* @param blockJson Parsed block metadata object that may be mutated.
|
|
27
|
+
* @param blockJsonRelativePath Relative path used in validation error messages.
|
|
28
|
+
* @returns The existing or newly created mutable `blockHooks` map.
|
|
29
|
+
* @throws {Error} When `blockHooks` exists but is not an object.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getMutableBlockHooks(blockJson: Record<string, unknown>, blockJsonRelativePath: string): Record<string, string>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an existing workspace block inventory entry by slug.
|
|
6
|
+
*
|
|
7
|
+
* @param inventory Parsed workspace inventory that owns available blocks.
|
|
8
|
+
* @param blockSlug Workspace block slug to locate.
|
|
9
|
+
* @returns The matching workspace block inventory entry.
|
|
10
|
+
* @throws {Error} When the block slug is not present in the inventory.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveWorkspaceBlock(inventory, blockSlug) {
|
|
13
|
+
const block = inventory.blocks.find((entry) => entry.slug === blockSlug);
|
|
14
|
+
if (!block) {
|
|
15
|
+
throw new Error(`Unknown workspace block "${blockSlug}". Choose one of: ${inventory.blocks.map((entry) => entry.slug).join(", ")}`);
|
|
16
|
+
}
|
|
17
|
+
return block;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read and validate a workspace block's `block.json` metadata from disk.
|
|
21
|
+
*
|
|
22
|
+
* @param projectDir Absolute workspace root directory.
|
|
23
|
+
* @param blockSlug Workspace block slug whose metadata should be read.
|
|
24
|
+
* @returns Parsed block metadata and the absolute `block.json` path.
|
|
25
|
+
* @throws {Error} When the file is missing or cannot be parsed as scaffold metadata.
|
|
26
|
+
*/
|
|
27
|
+
export function readWorkspaceBlockJson(projectDir, blockSlug) {
|
|
28
|
+
const blockJsonPath = path.join(projectDir, "src", "blocks", blockSlug, "block.json");
|
|
29
|
+
if (!fs.existsSync(blockJsonPath)) {
|
|
30
|
+
throw new Error(`Missing ${path.relative(projectDir, blockJsonPath)} for workspace block "${blockSlug}".`);
|
|
31
|
+
}
|
|
32
|
+
let blockJson;
|
|
33
|
+
try {
|
|
34
|
+
blockJson = parseScaffoldBlockMetadata(JSON.parse(fs.readFileSync(blockJsonPath, "utf8")));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(error instanceof Error
|
|
38
|
+
? `Failed to parse ${path.relative(projectDir, blockJsonPath)}: ${error.message}`
|
|
39
|
+
: `Failed to parse ${path.relative(projectDir, blockJsonPath)}.`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
blockJson,
|
|
43
|
+
blockJsonPath,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Return a mutable `blockHooks` object for a parsed block metadata document.
|
|
48
|
+
*
|
|
49
|
+
* @param blockJson Parsed block metadata object that may be mutated.
|
|
50
|
+
* @param blockJsonRelativePath Relative path used in validation error messages.
|
|
51
|
+
* @returns The existing or newly created mutable `blockHooks` map.
|
|
52
|
+
* @throws {Error} When `blockHooks` exists but is not an object.
|
|
53
|
+
*/
|
|
54
|
+
export function getMutableBlockHooks(blockJson, blockJsonRelativePath) {
|
|
55
|
+
const blockHooks = blockJson.blockHooks;
|
|
56
|
+
if (blockHooks === undefined) {
|
|
57
|
+
const nextHooks = {};
|
|
58
|
+
blockJson.blockHooks = nextHooks;
|
|
59
|
+
return nextHooks;
|
|
60
|
+
}
|
|
61
|
+
if (!blockHooks || typeof blockHooks !== "object" || Array.isArray(blockHooks)) {
|
|
62
|
+
throw new Error(`${blockJsonRelativePath} must define blockHooks as an object when present.`);
|
|
63
|
+
}
|
|
64
|
+
return blockHooks;
|
|
65
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
2
|
+
type ScaffoldFilesystemCollision = {
|
|
3
|
+
label: string;
|
|
4
|
+
relativePath: string;
|
|
5
|
+
};
|
|
6
|
+
type ScaffoldInventoryCollision<TEntry> = {
|
|
7
|
+
entries: readonly TEntry[];
|
|
8
|
+
exists: (entry: TEntry) => boolean;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Ensure scaffold targets do not already exist on disk or in workspace inventory.
|
|
13
|
+
*
|
|
14
|
+
* @param options Collision checks to run before writing scaffold files.
|
|
15
|
+
* @throws {Error} When a filesystem path or inventory entry already exists.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertScaffoldDoesNotExist<TEntry>(options: {
|
|
18
|
+
projectDir: string;
|
|
19
|
+
filesystemCollisions: readonly ScaffoldFilesystemCollision[];
|
|
20
|
+
inventoryCollision?: ScaffoldInventoryCollision<TEntry>;
|
|
21
|
+
}): void;
|
|
22
|
+
/**
|
|
23
|
+
* Ensure a block variation scaffold does not already exist.
|
|
24
|
+
*
|
|
25
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
26
|
+
* @param blockSlug Existing workspace block slug that owns the variation.
|
|
27
|
+
* @param variationSlug Normalized variation slug that would be created.
|
|
28
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
29
|
+
* @throws {Error} When the variation file or inventory entry already exists.
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertVariationDoesNotExist(projectDir: string, blockSlug: string, variationSlug: string, inventory: WorkspaceInventory): void;
|
|
32
|
+
/**
|
|
33
|
+
* Ensure a block style scaffold does not already exist.
|
|
34
|
+
*
|
|
35
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
36
|
+
* @param blockSlug Existing workspace block slug that owns the style.
|
|
37
|
+
* @param styleSlug Normalized style slug that would be created.
|
|
38
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
39
|
+
* @throws {Error} When the style file or inventory entry already exists.
|
|
40
|
+
*/
|
|
41
|
+
export declare function assertBlockStyleDoesNotExist(projectDir: string, blockSlug: string, styleSlug: string, inventory: WorkspaceInventory): void;
|
|
42
|
+
/**
|
|
43
|
+
* Ensure a block transform scaffold does not already exist.
|
|
44
|
+
*
|
|
45
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
46
|
+
* @param blockSlug Existing workspace block slug that owns the transform.
|
|
47
|
+
* @param transformSlug Normalized transform slug that would be created.
|
|
48
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
49
|
+
* @throws {Error} When the transform file or inventory entry already exists.
|
|
50
|
+
*/
|
|
51
|
+
export declare function assertBlockTransformDoesNotExist(projectDir: string, blockSlug: string, transformSlug: string, inventory: WorkspaceInventory): void;
|
|
52
|
+
/**
|
|
53
|
+
* Ensure a pattern scaffold does not already exist on disk or in inventory.
|
|
54
|
+
*
|
|
55
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
56
|
+
*
|
|
57
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
58
|
+
* @param patternSlug Normalized pattern slug that would be created.
|
|
59
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
60
|
+
* @throws {Error} When the pattern file or inventory entry already exists.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assertPatternDoesNotExist(projectDir: string, patternSlug: string, inventory: WorkspaceInventory): void;
|
|
63
|
+
/**
|
|
64
|
+
* Ensure a binding source scaffold does not already exist on disk or in inventory.
|
|
65
|
+
*
|
|
66
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
67
|
+
*
|
|
68
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
69
|
+
* @param bindingSourceSlug Normalized binding source slug that would be created.
|
|
70
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
71
|
+
* @throws {Error} When the binding directory or inventory entry already exists.
|
|
72
|
+
*/
|
|
73
|
+
export declare function assertBindingSourceDoesNotExist(projectDir: string, bindingSourceSlug: string, inventory: WorkspaceInventory): void;
|
|
74
|
+
/**
|
|
75
|
+
* Ensure a REST resource scaffold does not already exist on disk or in inventory.
|
|
76
|
+
*
|
|
77
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
78
|
+
*
|
|
79
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
80
|
+
* @param restResourceSlug Normalized REST resource slug that would be created.
|
|
81
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
82
|
+
* @throws {Error} When REST resource files or inventory entry already exist.
|
|
83
|
+
*/
|
|
84
|
+
export declare function assertRestResourceDoesNotExist(projectDir: string, restResourceSlug: string, inventory: WorkspaceInventory): void;
|
|
85
|
+
/**
|
|
86
|
+
* Ensure a DataViews admin screen scaffold does not already exist on disk or in
|
|
87
|
+
* the workspace inventory.
|
|
88
|
+
*
|
|
89
|
+
* @param projectDir Workspace root directory.
|
|
90
|
+
* @param adminViewSlug Normalized admin screen slug.
|
|
91
|
+
* @param inventory Parsed workspace inventory.
|
|
92
|
+
* @throws {Error} When the directory, PHP bootstrap, or inventory entry already exists.
|
|
93
|
+
*/
|
|
94
|
+
export declare function assertAdminViewDoesNotExist(projectDir: string, adminViewSlug: string, inventory: WorkspaceInventory): void;
|
|
95
|
+
/**
|
|
96
|
+
* Ensure a workflow ability scaffold does not already exist on disk or in the
|
|
97
|
+
* workspace inventory.
|
|
98
|
+
*
|
|
99
|
+
* The check covers the generated `src/abilities/<slug>` directory,
|
|
100
|
+
* `inc/abilities/<slug>.php`, and any matching `inventory.abilities` entry.
|
|
101
|
+
*
|
|
102
|
+
* @param projectDir Workspace root directory.
|
|
103
|
+
* @param abilitySlug Normalized workflow ability slug.
|
|
104
|
+
* @param inventory Parsed workspace inventory.
|
|
105
|
+
* @throws {Error} When the ability directory, PHP bootstrap, or inventory entry already exists.
|
|
106
|
+
*/
|
|
107
|
+
export declare function assertAbilityDoesNotExist(projectDir: string, abilitySlug: string, inventory: WorkspaceInventory): void;
|
|
108
|
+
/**
|
|
109
|
+
* Ensure an AI feature scaffold does not already exist on disk or in inventory.
|
|
110
|
+
*
|
|
111
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
112
|
+
*
|
|
113
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
114
|
+
* @param aiFeatureSlug Normalized AI feature slug that would be created.
|
|
115
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
116
|
+
* @throws {Error} When AI feature files or inventory entry already exist.
|
|
117
|
+
*/
|
|
118
|
+
export declare function assertAiFeatureDoesNotExist(projectDir: string, aiFeatureSlug: string, inventory: WorkspaceInventory): void;
|
|
119
|
+
/**
|
|
120
|
+
* Ensure an editor plugin scaffold does not already exist on disk or in the
|
|
121
|
+
* workspace inventory.
|
|
122
|
+
*
|
|
123
|
+
* @param projectDir Workspace root directory.
|
|
124
|
+
* @param editorPluginSlug Normalized editor plugin slug.
|
|
125
|
+
* @param inventory Parsed workspace inventory.
|
|
126
|
+
* @throws {Error} When the directory or inventory entry already exists.
|
|
127
|
+
*/
|
|
128
|
+
export declare function assertEditorPluginDoesNotExist(projectDir: string, editorPluginSlug: string, inventory: WorkspaceInventory): void;
|
|
129
|
+
export {};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Ensure scaffold targets do not already exist on disk or in workspace inventory.
|
|
5
|
+
*
|
|
6
|
+
* @param options Collision checks to run before writing scaffold files.
|
|
7
|
+
* @throws {Error} When a filesystem path or inventory entry already exists.
|
|
8
|
+
*/
|
|
9
|
+
export function assertScaffoldDoesNotExist(options) {
|
|
10
|
+
for (const collision of options.filesystemCollisions) {
|
|
11
|
+
const targetPath = path.join(options.projectDir, collision.relativePath);
|
|
12
|
+
if (fs.existsSync(targetPath)) {
|
|
13
|
+
throw new Error(`${collision.label} already exists at ${path.relative(options.projectDir, targetPath)}. Choose a different name.`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (options.inventoryCollision &&
|
|
17
|
+
options.inventoryCollision.entries.some(options.inventoryCollision.exists)) {
|
|
18
|
+
throw new Error(options.inventoryCollision.message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Ensure a block variation scaffold does not already exist.
|
|
23
|
+
*
|
|
24
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
25
|
+
* @param blockSlug Existing workspace block slug that owns the variation.
|
|
26
|
+
* @param variationSlug Normalized variation slug that would be created.
|
|
27
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
28
|
+
* @throws {Error} When the variation file or inventory entry already exists.
|
|
29
|
+
*/
|
|
30
|
+
export function assertVariationDoesNotExist(projectDir, blockSlug, variationSlug, inventory) {
|
|
31
|
+
assertScaffoldDoesNotExist({
|
|
32
|
+
filesystemCollisions: [
|
|
33
|
+
{
|
|
34
|
+
label: "A variation",
|
|
35
|
+
relativePath: path.join("src", "blocks", blockSlug, "variations", `${variationSlug}.ts`),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
inventoryCollision: {
|
|
39
|
+
entries: inventory.variations,
|
|
40
|
+
exists: (entry) => entry.block === blockSlug && entry.slug === variationSlug,
|
|
41
|
+
message: `A variation inventory entry already exists for ${blockSlug}/${variationSlug}. Choose a different name.`,
|
|
42
|
+
},
|
|
43
|
+
projectDir,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ensure a block style scaffold does not already exist.
|
|
48
|
+
*
|
|
49
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
50
|
+
* @param blockSlug Existing workspace block slug that owns the style.
|
|
51
|
+
* @param styleSlug Normalized style slug that would be created.
|
|
52
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
53
|
+
* @throws {Error} When the style file or inventory entry already exists.
|
|
54
|
+
*/
|
|
55
|
+
export function assertBlockStyleDoesNotExist(projectDir, blockSlug, styleSlug, inventory) {
|
|
56
|
+
assertScaffoldDoesNotExist({
|
|
57
|
+
filesystemCollisions: [
|
|
58
|
+
{
|
|
59
|
+
label: "A block style",
|
|
60
|
+
relativePath: path.join("src", "blocks", blockSlug, "styles", `${styleSlug}.ts`),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
inventoryCollision: {
|
|
64
|
+
entries: inventory.blockStyles,
|
|
65
|
+
exists: (entry) => entry.block === blockSlug && entry.slug === styleSlug,
|
|
66
|
+
message: `A block style inventory entry already exists for ${blockSlug}/${styleSlug}. Choose a different name.`,
|
|
67
|
+
},
|
|
68
|
+
projectDir,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ensure a block transform scaffold does not already exist.
|
|
73
|
+
*
|
|
74
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
75
|
+
* @param blockSlug Existing workspace block slug that owns the transform.
|
|
76
|
+
* @param transformSlug Normalized transform slug that would be created.
|
|
77
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
78
|
+
* @throws {Error} When the transform file or inventory entry already exists.
|
|
79
|
+
*/
|
|
80
|
+
export function assertBlockTransformDoesNotExist(projectDir, blockSlug, transformSlug, inventory) {
|
|
81
|
+
assertScaffoldDoesNotExist({
|
|
82
|
+
filesystemCollisions: [
|
|
83
|
+
{
|
|
84
|
+
label: "A block transform",
|
|
85
|
+
relativePath: path.join("src", "blocks", blockSlug, "transforms", `${transformSlug}.ts`),
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
inventoryCollision: {
|
|
89
|
+
entries: inventory.blockTransforms,
|
|
90
|
+
exists: (entry) => entry.block === blockSlug && entry.slug === transformSlug,
|
|
91
|
+
message: `A block transform inventory entry already exists for ${blockSlug}/${transformSlug}. Choose a different name.`,
|
|
92
|
+
},
|
|
93
|
+
projectDir,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Ensure a pattern scaffold does not already exist on disk or in inventory.
|
|
98
|
+
*
|
|
99
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
100
|
+
*
|
|
101
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
102
|
+
* @param patternSlug Normalized pattern slug that would be created.
|
|
103
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
104
|
+
* @throws {Error} When the pattern file or inventory entry already exists.
|
|
105
|
+
*/
|
|
106
|
+
export function assertPatternDoesNotExist(projectDir, patternSlug, inventory) {
|
|
107
|
+
assertScaffoldDoesNotExist({
|
|
108
|
+
filesystemCollisions: [
|
|
109
|
+
{
|
|
110
|
+
label: "A pattern",
|
|
111
|
+
relativePath: path.join("src", "patterns", `${patternSlug}.php`),
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
inventoryCollision: {
|
|
115
|
+
entries: inventory.patterns,
|
|
116
|
+
exists: (entry) => entry.slug === patternSlug,
|
|
117
|
+
message: `A pattern inventory entry already exists for ${patternSlug}. Choose a different name.`,
|
|
118
|
+
},
|
|
119
|
+
projectDir,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Ensure a binding source scaffold does not already exist on disk or in inventory.
|
|
124
|
+
*
|
|
125
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
126
|
+
*
|
|
127
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
128
|
+
* @param bindingSourceSlug Normalized binding source slug that would be created.
|
|
129
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
130
|
+
* @throws {Error} When the binding directory or inventory entry already exists.
|
|
131
|
+
*/
|
|
132
|
+
export function assertBindingSourceDoesNotExist(projectDir, bindingSourceSlug, inventory) {
|
|
133
|
+
assertScaffoldDoesNotExist({
|
|
134
|
+
filesystemCollisions: [
|
|
135
|
+
{
|
|
136
|
+
label: "A binding source",
|
|
137
|
+
relativePath: path.join("src", "bindings", bindingSourceSlug),
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
inventoryCollision: {
|
|
141
|
+
entries: inventory.bindingSources,
|
|
142
|
+
exists: (entry) => entry.slug === bindingSourceSlug,
|
|
143
|
+
message: `A binding source inventory entry already exists for ${bindingSourceSlug}. Choose a different name.`,
|
|
144
|
+
},
|
|
145
|
+
projectDir,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Ensure a REST resource scaffold does not already exist on disk or in inventory.
|
|
150
|
+
*
|
|
151
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
152
|
+
*
|
|
153
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
154
|
+
* @param restResourceSlug Normalized REST resource slug that would be created.
|
|
155
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
156
|
+
* @throws {Error} When REST resource files or inventory entry already exist.
|
|
157
|
+
*/
|
|
158
|
+
export function assertRestResourceDoesNotExist(projectDir, restResourceSlug, inventory) {
|
|
159
|
+
assertScaffoldDoesNotExist({
|
|
160
|
+
filesystemCollisions: [
|
|
161
|
+
{
|
|
162
|
+
label: "A REST resource",
|
|
163
|
+
relativePath: path.join("src", "rest", restResourceSlug),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
label: "A REST resource bootstrap",
|
|
167
|
+
relativePath: path.join("inc", "rest", `${restResourceSlug}.php`),
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
inventoryCollision: {
|
|
171
|
+
entries: inventory.restResources,
|
|
172
|
+
exists: (entry) => entry.slug === restResourceSlug,
|
|
173
|
+
message: `A REST resource inventory entry already exists for ${restResourceSlug}. Choose a different name.`,
|
|
174
|
+
},
|
|
175
|
+
projectDir,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Ensure a DataViews admin screen scaffold does not already exist on disk or in
|
|
180
|
+
* the workspace inventory.
|
|
181
|
+
*
|
|
182
|
+
* @param projectDir Workspace root directory.
|
|
183
|
+
* @param adminViewSlug Normalized admin screen slug.
|
|
184
|
+
* @param inventory Parsed workspace inventory.
|
|
185
|
+
* @throws {Error} When the directory, PHP bootstrap, or inventory entry already exists.
|
|
186
|
+
*/
|
|
187
|
+
export function assertAdminViewDoesNotExist(projectDir, adminViewSlug, inventory) {
|
|
188
|
+
assertScaffoldDoesNotExist({
|
|
189
|
+
filesystemCollisions: [
|
|
190
|
+
{
|
|
191
|
+
label: "An admin view",
|
|
192
|
+
relativePath: path.join("src", "admin-views", adminViewSlug),
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
label: "An admin view bootstrap",
|
|
196
|
+
relativePath: path.join("inc", "admin-views", `${adminViewSlug}.php`),
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
inventoryCollision: {
|
|
200
|
+
entries: inventory.adminViews,
|
|
201
|
+
exists: (entry) => entry.slug === adminViewSlug,
|
|
202
|
+
message: `An admin view inventory entry already exists for ${adminViewSlug}. Choose a different name.`,
|
|
203
|
+
},
|
|
204
|
+
projectDir,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Ensure a workflow ability scaffold does not already exist on disk or in the
|
|
209
|
+
* workspace inventory.
|
|
210
|
+
*
|
|
211
|
+
* The check covers the generated `src/abilities/<slug>` directory,
|
|
212
|
+
* `inc/abilities/<slug>.php`, and any matching `inventory.abilities` entry.
|
|
213
|
+
*
|
|
214
|
+
* @param projectDir Workspace root directory.
|
|
215
|
+
* @param abilitySlug Normalized workflow ability slug.
|
|
216
|
+
* @param inventory Parsed workspace inventory.
|
|
217
|
+
* @throws {Error} When the ability directory, PHP bootstrap, or inventory entry already exists.
|
|
218
|
+
*/
|
|
219
|
+
export function assertAbilityDoesNotExist(projectDir, abilitySlug, inventory) {
|
|
220
|
+
assertScaffoldDoesNotExist({
|
|
221
|
+
filesystemCollisions: [
|
|
222
|
+
{
|
|
223
|
+
label: "An ability scaffold",
|
|
224
|
+
relativePath: path.join("src", "abilities", abilitySlug),
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
label: "An ability bootstrap",
|
|
228
|
+
relativePath: path.join("inc", "abilities", `${abilitySlug}.php`),
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
inventoryCollision: {
|
|
232
|
+
entries: inventory.abilities,
|
|
233
|
+
exists: (entry) => entry.slug === abilitySlug,
|
|
234
|
+
message: `An ability inventory entry already exists for ${abilitySlug}. Choose a different name.`,
|
|
235
|
+
},
|
|
236
|
+
projectDir,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Ensure an AI feature scaffold does not already exist on disk or in inventory.
|
|
241
|
+
*
|
|
242
|
+
* Delegates filesystem and inventory checks to `assertScaffoldDoesNotExist`.
|
|
243
|
+
*
|
|
244
|
+
* @param projectDir Absolute workspace root used to resolve scaffold paths.
|
|
245
|
+
* @param aiFeatureSlug Normalized AI feature slug that would be created.
|
|
246
|
+
* @param inventory Current workspace inventory used for duplicate detection.
|
|
247
|
+
* @throws {Error} When AI feature files or inventory entry already exist.
|
|
248
|
+
*/
|
|
249
|
+
export function assertAiFeatureDoesNotExist(projectDir, aiFeatureSlug, inventory) {
|
|
250
|
+
assertScaffoldDoesNotExist({
|
|
251
|
+
filesystemCollisions: [
|
|
252
|
+
{
|
|
253
|
+
label: "An AI feature",
|
|
254
|
+
relativePath: path.join("src", "ai-features", aiFeatureSlug),
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: "An AI feature bootstrap",
|
|
258
|
+
relativePath: path.join("inc", "ai-features", `${aiFeatureSlug}.php`),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
inventoryCollision: {
|
|
262
|
+
entries: inventory.aiFeatures,
|
|
263
|
+
exists: (entry) => entry.slug === aiFeatureSlug,
|
|
264
|
+
message: `An AI feature inventory entry already exists for ${aiFeatureSlug}. Choose a different name.`,
|
|
265
|
+
},
|
|
266
|
+
projectDir,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Ensure an editor plugin scaffold does not already exist on disk or in the
|
|
271
|
+
* workspace inventory.
|
|
272
|
+
*
|
|
273
|
+
* @param projectDir Workspace root directory.
|
|
274
|
+
* @param editorPluginSlug Normalized editor plugin slug.
|
|
275
|
+
* @param inventory Parsed workspace inventory.
|
|
276
|
+
* @throws {Error} When the directory or inventory entry already exists.
|
|
277
|
+
*/
|
|
278
|
+
export function assertEditorPluginDoesNotExist(projectDir, editorPluginSlug, inventory) {
|
|
279
|
+
assertScaffoldDoesNotExist({
|
|
280
|
+
filesystemCollisions: [
|
|
281
|
+
{
|
|
282
|
+
label: "An editor plugin",
|
|
283
|
+
relativePath: path.join("src", "editor-plugins", editorPluginSlug),
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
inventoryCollision: {
|
|
287
|
+
entries: inventory.editorPlugins,
|
|
288
|
+
exists: (entry) => entry.slug === editorPluginSlug,
|
|
289
|
+
message: `An editor plugin inventory entry already exists for ${editorPluginSlug}. Choose a different name.`,
|
|
290
|
+
},
|
|
291
|
+
projectDir,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WorkspaceMutationSnapshot } from "./cli-add-types.js";
|
|
2
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the primary PHP bootstrap file for an official workspace.
|
|
5
|
+
*
|
|
6
|
+
* @param workspace Workspace metadata that provides `packageName` and `projectDir`.
|
|
7
|
+
* @returns Absolute path to `<packageBase>.php` in the workspace root.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getWorkspaceBootstrapPath(workspace: WorkspaceProject): string;
|
|
10
|
+
/**
|
|
11
|
+
* Apply a text transform to an existing file only when the contents change.
|
|
12
|
+
*/
|
|
13
|
+
export declare function patchFile(filePath: string, transform: (source: string) => string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Read a file when it exists and otherwise return `null`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readOptionalFile(filePath: string): Promise<string | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Restore a file to its captured source, deleting it when the snapshot was `null`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function restoreOptionalFile(filePath: string, source: string | null): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Capture the current contents of a set of workspace files for rollback.
|
|
24
|
+
*/
|
|
25
|
+
export declare function snapshotWorkspaceFiles(filePaths: string[]): Promise<WorkspaceMutationSnapshot["fileSources"]>;
|
|
26
|
+
/**
|
|
27
|
+
* Undo a partially applied workspace mutation from a captured snapshot.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rollbackWorkspaceMutation(snapshot: WorkspaceMutationSnapshot): Promise<void>;
|