@superblocksteam/vite-plugin-file-sync 2.0.6 → 2.0.7
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/ai-service/app-interface/linter.d.ts +7 -2
- package/dist/ai-service/app-interface/linter.d.ts.map +1 -1
- package/dist/ai-service/app-interface/linter.js +52 -41
- package/dist/ai-service/app-interface/linter.js.map +1 -1
- package/dist/ai-service/app-interface/shell.d.ts +2 -0
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +13 -4
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/const.d.ts +2 -0
- package/dist/ai-service/const.d.ts.map +1 -1
- package/dist/ai-service/const.js +2 -0
- package/dist/ai-service/const.js.map +1 -1
- package/dist/ai-service/eval/template-renderer.d.ts.map +1 -1
- package/dist/ai-service/eval/template-renderer.js +1 -1
- package/dist/ai-service/eval/template-renderer.js.map +1 -1
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +4 -12
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/integrations/from-prompt-context.d.ts +1 -1
- package/dist/ai-service/integrations/from-prompt-context.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbButtonPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbCheckboxPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbColumnPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbContainerPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbDatePickerPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbDropdownPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbIconPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbImagePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbInputPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbModalPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbPagePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSectionPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSlideoutPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbSwitchPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbTablePropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.d.ts +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.js +2 -2
- package/dist/ai-service/prompts/generated/library-components/SbTextPropsDocs.js.map +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/Dim.js +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/SbEventFlow.js +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.d.ts +2 -0
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.d.ts.map +1 -0
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.js +6 -0
- package/dist/ai-service/prompts/generated/library-typedefs/TextStyleWithVariant.js.map +1 -0
- package/dist/ai-service/prompts/generated/library-typedefs/index.d.ts +1 -0
- package/dist/ai-service/prompts/generated/library-typedefs/index.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/library-typedefs/index.js +1 -0
- package/dist/ai-service/prompts/generated/library-typedefs/index.js.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/full-examples.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.d.ts +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.d.ts.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.js +2 -2
- package/dist/ai-service/prompts/generated/subprompts/superblocks-api.js.map +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-components-rules.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-custom-components.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-data-filtering.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-event-flow.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-forms.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-layouts.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-page.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-rbac.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-routes.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-state.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/superblocks-theming.js +1 -1
- package/dist/ai-service/prompts/generated/subprompts/system.js +1 -1
- package/dist/ai-service/prompts/system.d.ts.map +1 -1
- package/dist/ai-service/prompts/system.js +4 -0
- package/dist/ai-service/prompts/system.js.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts +2 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +13 -2
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +75 -24
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js +18 -6
- package/dist/ai-service/state-machine/handlers/runtime-reviewing.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/rate-limiting.d.ts +6 -0
- package/dist/ai-service/state-machine/helpers/rate-limiting.d.ts.map +1 -0
- package/dist/ai-service/state-machine/helpers/rate-limiting.js +26 -0
- package/dist/ai-service/state-machine/helpers/rate-limiting.js.map +1 -0
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.d.ts +3 -0
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.d.ts.map +1 -0
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.js +851 -0
- package/dist/ai-service/test-utils/app-generation-mocks/orders-app.js.map +1 -0
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.d.ts +3 -0
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.d.ts.map +1 -0
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.js +111 -0
- package/dist/ai-service/test-utils/app-generation-mocks/smoketest.js.map +1 -0
- package/dist/ai-service/test-utils/mock-utils.d.ts +22 -0
- package/dist/ai-service/test-utils/mock-utils.d.ts.map +1 -0
- package/dist/ai-service/test-utils/mock-utils.js +46 -0
- package/dist/ai-service/test-utils/mock-utils.js.map +1 -0
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.d.ts.map +1 -1
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.js +3 -2
- package/dist/ai-service/transform/add-metadata-to-api-yaml/transformer.js.map +1 -1
- package/dist/ai-service/transform/shared.d.ts.map +1 -1
- package/dist/ai-service/transform/shared.js +9 -8
- package/dist/ai-service/transform/shared.js.map +1 -1
- package/dist/ai-service/types.d.ts +23 -10
- package/dist/ai-service/types.d.ts.map +1 -1
- package/dist/ai-service/types.js.map +1 -1
- package/dist/binding-extraction/extract-control-block-identifiers.d.ts +1 -1
- package/dist/binding-extraction/extract-control-block-identifiers.d.ts.map +1 -1
- package/dist/binding-extraction/extract-control-block-identifiers.js +15 -13
- package/dist/binding-extraction/extract-control-block-identifiers.js.map +1 -1
- package/dist/binding-extraction/extract-identifiers.d.ts +3 -3
- package/dist/binding-extraction/extract-identifiers.d.ts.map +1 -1
- package/dist/binding-extraction/extract-identifiers.js +83 -52
- package/dist/binding-extraction/extract-identifiers.js.map +1 -1
- package/dist/binding-extraction/index.d.ts +2 -0
- package/dist/binding-extraction/index.d.ts.map +1 -0
- package/dist/binding-extraction/index.js +2 -0
- package/dist/binding-extraction/index.js.map +1 -0
- package/dist/binding-extraction/shared.d.ts +0 -199
- package/dist/binding-extraction/shared.d.ts.map +1 -1
- package/dist/binding-extraction/shared.js +1 -42
- package/dist/binding-extraction/shared.js.map +1 -1
- package/dist/codegen.js +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/components-manager.d.ts +4 -1
- package/dist/components-manager.d.ts.map +1 -1
- package/dist/components-manager.js +34 -4
- package/dist/components-manager.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +44 -17
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +4 -0
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +10 -0
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/file-system-manager.d.ts +52 -39
- package/dist/file-system-manager.d.ts.map +1 -1
- package/dist/file-system-manager.js +659 -532
- package/dist/file-system-manager.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inject-index-vite-plugin.d.ts +0 -2
- package/dist/inject-index-vite-plugin.d.ts.map +1 -1
- package/dist/inject-index-vite-plugin.js +2 -2
- package/dist/inject-index-vite-plugin.js.map +1 -1
- package/dist/injected-index.d.ts +2 -2
- package/dist/injected-index.d.ts.map +1 -1
- package/dist/injected-index.js.map +1 -1
- package/dist/lock-service/index.d.ts.map +1 -1
- package/dist/lock-service/index.js +13 -2
- package/dist/lock-service/index.js.map +1 -1
- package/dist/operations/operation-processor.d.ts +24 -0
- package/dist/operations/operation-processor.d.ts.map +1 -0
- package/dist/operations/operation-processor.js +80 -0
- package/dist/operations/operation-processor.js.map +1 -0
- package/dist/operations/types.d.ts +8 -0
- package/dist/operations/types.d.ts.map +1 -0
- package/dist/operations/types.js +2 -0
- package/dist/operations/types.js.map +1 -0
- package/dist/parsing/computed/to-code-computed.d.ts.map +1 -1
- package/dist/parsing/computed/to-code-computed.js +7 -3
- package/dist/parsing/computed/to-code-computed.js.map +1 -1
- package/dist/parsing/entity/to-value-entity.js.map +1 -1
- package/dist/parsing/events/to-code-events.d.ts +1 -1
- package/dist/parsing/events/to-code-events.d.ts.map +1 -1
- package/dist/parsing/events/to-code-events.js +15 -4
- package/dist/parsing/events/to-code-events.js.map +1 -1
- package/dist/parsing/events/to-value-events.d.ts.map +1 -1
- package/dist/parsing/events/to-value-events.js +47 -0
- package/dist/parsing/events/to-value-events.js.map +1 -1
- package/dist/parsing/index.d.ts +3 -0
- package/dist/parsing/index.d.ts.map +1 -0
- package/dist/parsing/index.js +3 -0
- package/dist/parsing/index.js.map +1 -0
- package/dist/parsing/properties.js.map +1 -1
- package/dist/parsing/template/index.js +1 -1
- package/dist/parsing/template/index.js.map +1 -1
- package/dist/parsing/template/to-code-template.d.ts +2 -1
- package/dist/parsing/template/to-code-template.d.ts.map +1 -1
- package/dist/parsing/template/to-code-template.js +2 -2
- package/dist/parsing/template/to-code-template.js.map +1 -1
- package/dist/plugin-options.d.ts +0 -2
- package/dist/plugin-options.d.ts.map +1 -1
- package/dist/plugin-options.js.map +1 -1
- package/dist/refactor/blocks.d.ts.map +1 -1
- package/dist/refactor/blocks.js +2 -69
- package/dist/refactor/blocks.js.map +1 -1
- package/dist/refactor/entities.d.ts +6 -0
- package/dist/refactor/entities.d.ts.map +1 -0
- package/dist/refactor/entities.js +62 -0
- package/dist/refactor/entities.js.map +1 -0
- package/dist/refactor/javascript.d.ts +0 -4
- package/dist/refactor/javascript.d.ts.map +1 -1
- package/dist/refactor/javascript.js +0 -8
- package/dist/refactor/javascript.js.map +1 -1
- package/dist/rename-manager.d.ts +0 -5
- package/dist/rename-manager.d.ts.map +1 -1
- package/dist/rename-manager.js +1 -27
- package/dist/rename-manager.js.map +1 -1
- package/dist/routing.d.ts +2 -2
- package/dist/routing.d.ts.map +1 -1
- package/dist/routing.js +21 -1
- package/dist/routing.js.map +1 -1
- package/dist/sb-scope-manager.d.ts +1 -1
- package/dist/sb-scope-manager.d.ts.map +1 -1
- package/dist/sb-scope-manager.js +10 -0
- package/dist/sb-scope-manager.js.map +1 -1
- package/dist/socket-manager.d.ts +2 -2
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +7 -5
- package/dist/socket-manager.js.map +1 -1
- package/dist/source-tracker.d.ts +20 -20
- package/dist/source-tracker.d.ts.map +1 -1
- package/dist/source-tracker.js +33 -17
- package/dist/source-tracker.js.map +1 -1
- package/dist/sync-service/hash-cache.d.ts +1 -0
- package/dist/sync-service/hash-cache.d.ts.map +1 -1
- package/dist/sync-service/hash-cache.js +4 -0
- package/dist/sync-service/hash-cache.js.map +1 -1
- package/dist/sync-service/index.d.ts +4 -0
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +30 -2
- package/dist/sync-service/index.js.map +1 -1
- package/dist/sync-service/list-dir.js +1 -1
- package/dist/sync-service/list-dir.js.map +1 -1
- package/dist/sync-service/server-rpc/client.d.ts.map +1 -1
- package/dist/sync-service/server-rpc/client.js +4 -1
- package/dist/sync-service/server-rpc/client.js.map +1 -1
- package/dist/util/logger.d.ts +13 -17
- package/dist/util/logger.d.ts.map +1 -1
- package/dist/util/logger.js +34 -44
- package/dist/util/logger.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +8 -0
- package/dist/util.js.map +1 -1
- package/package.json +15 -6
- package/dist/util/tracing.d.ts +0 -4
- package/dist/util/tracing.d.ts.map +0 -1
- package/dist/util/tracing.js +0 -56
- package/dist/util/tracing.js.map +0 -1
|
@@ -9,10 +9,12 @@ import { glob } from "glob";
|
|
|
9
9
|
import { isEqual } from "lodash-es";
|
|
10
10
|
import yaml from "yaml";
|
|
11
11
|
import { generateJSXAttribute } from "./codegen.js";
|
|
12
|
+
import { ComponentsManager } from "./components-manager.js";
|
|
12
13
|
import { addLegacyCustomComponentVariables, modifyLegacyCustomComponentElements, modifyLegacyCustomComponentImports, } from "./custom-components.js";
|
|
13
14
|
import { applyErrorHandling, } from "./errors/error-handler.js";
|
|
14
|
-
import { getPageFolder, PAGES_DIRECTORY, ROUTES_FILE, SCOPE_FILE, } from "./file-system-helpers.js";
|
|
15
|
+
import { getApiFilePath, getPageFolder, isPageFilePath, PAGES_DIRECTORY, ROUTES_FILE, SCOPE_FILE, } from "./file-system-helpers.js";
|
|
15
16
|
import { generate } from "./generate.js";
|
|
17
|
+
import { OperationProcessor } from "./operations/operation-processor.js";
|
|
16
18
|
import { doesElementHaveBinding } from "./parsing/bindings.js";
|
|
17
19
|
import { getSbElementId } from "./parsing/ids.js";
|
|
18
20
|
import { makeJSXAttribute } from "./parsing/jsx.js";
|
|
@@ -23,6 +25,7 @@ import { RenameManager } from "./rename-manager.js";
|
|
|
23
25
|
import { SourceTracker } from "./source-tracker.js";
|
|
24
26
|
import { traverse } from "./traverse.js";
|
|
25
27
|
import { getErrorMeta, getLogger } from "./util/logger.js";
|
|
28
|
+
import { getPageName } from "./util.js";
|
|
26
29
|
const SUPPORTED_FILETYPES = [
|
|
27
30
|
{
|
|
28
31
|
type: "tsx",
|
|
@@ -44,26 +47,41 @@ const SUPPORTED_FILETYPES = [
|
|
|
44
47
|
type: "js-api-step",
|
|
45
48
|
extension: ".js",
|
|
46
49
|
},
|
|
50
|
+
{
|
|
51
|
+
type: "json",
|
|
52
|
+
extension: ".json",
|
|
53
|
+
},
|
|
47
54
|
];
|
|
48
55
|
const APP_THEME_FILE_NAME = "appTheme.ts";
|
|
49
|
-
export class
|
|
56
|
+
export class FileSystemManager extends TracedEventEmitter {
|
|
50
57
|
rootDir;
|
|
51
58
|
tsFiles = {};
|
|
52
59
|
apiFiles = {};
|
|
53
60
|
sourceTracker;
|
|
54
61
|
fsOperationQueue;
|
|
62
|
+
operationProcessor;
|
|
55
63
|
generationNumberSequence;
|
|
56
64
|
routes = {};
|
|
65
|
+
routeChangesQueue = [];
|
|
57
66
|
watcher;
|
|
58
67
|
registeredComponentPaths = {};
|
|
59
68
|
renameManager = new RenameManager();
|
|
60
69
|
_tracer;
|
|
70
|
+
transactionNonce = Date.now();
|
|
71
|
+
pendingTransactions = new Set();
|
|
72
|
+
processedTransactions = [];
|
|
61
73
|
constructor(fsOperationQueue, generationNumberSequence, tracer) {
|
|
62
74
|
super(tracer, { captureRejections: true });
|
|
63
75
|
this.rootDir = "/";
|
|
64
76
|
this.fsOperationQueue = fsOperationQueue;
|
|
65
77
|
this.generationNumberSequence = generationNumberSequence;
|
|
66
78
|
this._tracer = tracer;
|
|
79
|
+
// intentionally a new queue here, we don't want to share the queue with the fsOperationQueue
|
|
80
|
+
this.operationProcessor = new OperationProcessor({
|
|
81
|
+
batchWindowMs: 50,
|
|
82
|
+
maxBatchSize: 100,
|
|
83
|
+
});
|
|
84
|
+
this.operationProcessor.disable();
|
|
67
85
|
applyErrorHandling(this, {
|
|
68
86
|
watch: { operation: "editing from code" },
|
|
69
87
|
handleCreatePage: { operation: "creating a page" },
|
|
@@ -109,27 +127,7 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
109
127
|
}
|
|
110
128
|
return path.join(this.rootDir, "App.tsx");
|
|
111
129
|
}
|
|
112
|
-
|
|
113
|
-
if (!this.rootDir) {
|
|
114
|
-
throw new Error("Root directory not set");
|
|
115
|
-
}
|
|
116
|
-
const { api: apiContents, stepPathMap } = content;
|
|
117
|
-
const pageName = getPageName(path);
|
|
118
|
-
let scopeId = this.sourceTracker?.getScopeDefinitionForPage(pageName)?.id;
|
|
119
|
-
if (!scopeId) {
|
|
120
|
-
console.warn("Scope ID not found for API", apiContents.metadata.name);
|
|
121
|
-
scopeId = "";
|
|
122
|
-
}
|
|
123
|
-
const updatedApi = {
|
|
124
|
-
apiPb: yaml.parse(JSON.stringify(apiContents)),
|
|
125
|
-
pageName,
|
|
126
|
-
stepPathMap,
|
|
127
|
-
scopeId,
|
|
128
|
-
};
|
|
129
|
-
const isNewApi = !this.apiFiles[path];
|
|
130
|
-
this.apiFiles[path] = updatedApi;
|
|
131
|
-
return { updatedApi, pageName, isNewApi };
|
|
132
|
-
};
|
|
130
|
+
// MARK: core setup/init
|
|
133
131
|
async watch(watcher, rootPath) {
|
|
134
132
|
const logger = getLogger();
|
|
135
133
|
this.rootDir = rootPath;
|
|
@@ -215,178 +213,10 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
215
213
|
return;
|
|
216
214
|
const { type, path } = file;
|
|
217
215
|
if (type === "api") {
|
|
218
|
-
this.
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
const handleFileChange = async (event, filePath) => {
|
|
222
|
-
logger.info(`File changed: ${filePath}, event: ${event}`);
|
|
223
|
-
switch (event) {
|
|
224
|
-
case "add": {
|
|
225
|
-
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
226
|
-
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const data = await readFile(filePath);
|
|
230
|
-
if (typeof data !== "string")
|
|
231
|
-
return;
|
|
232
|
-
// if we match pages/*/index.tsx, we want to emit an addPage event
|
|
233
|
-
if (/pages\/.*\/index\.tsx/.test(filePath)) {
|
|
234
|
-
const file = await readFile(filePath);
|
|
235
|
-
if (!file) {
|
|
236
|
-
logger.error(`Failed to read file: ${filePath}`);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
if (!(filePath in this.tsFiles)) {
|
|
240
|
-
this.tsFiles[filePath] = file;
|
|
241
|
-
this.handleNonVisualChangeByDeletingIds(filePath, file);
|
|
242
|
-
this.emit("addPage", filePath);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
switch (fileType.type) {
|
|
246
|
-
case "api":
|
|
247
|
-
case "python-api-step":
|
|
248
|
-
case "js-api-step": {
|
|
249
|
-
await this.processApiUpdates(filePath, fileType);
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
case "change": {
|
|
256
|
-
// Special routes file handling
|
|
257
|
-
if (filePath === routePath) {
|
|
258
|
-
const data = await readFile(filePath);
|
|
259
|
-
try {
|
|
260
|
-
this.routes = JSON.parse(data);
|
|
261
|
-
this.emit("routesChanged", this.routes);
|
|
262
|
-
}
|
|
263
|
-
catch (e) {
|
|
264
|
-
logger.error("Error parsing routes file", getErrorMeta(e));
|
|
265
|
-
}
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
269
|
-
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
const data = await readFile(filePath);
|
|
273
|
-
if (typeof data !== "string")
|
|
274
|
-
return;
|
|
275
|
-
switch (fileType.type) {
|
|
276
|
-
case "tsx":
|
|
277
|
-
case "scope":
|
|
278
|
-
{
|
|
279
|
-
if (!(filePath in this.tsFiles && this.tsFiles[filePath] === data)) {
|
|
280
|
-
logger.info(`File changed: ${filePath} updating AST tracker`);
|
|
281
|
-
this.tsFiles[filePath] = data;
|
|
282
|
-
// only update the source tracker if the file is different
|
|
283
|
-
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
284
|
-
this.emit("fileChanged", filePath, data, true);
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
logger.info(`File unchanged from last tracked state: ${filePath}`);
|
|
288
|
-
this.emit("fileChanged", filePath, data, false);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
break;
|
|
292
|
-
case "api":
|
|
293
|
-
case "python-api-step":
|
|
294
|
-
case "js-api-step":
|
|
295
|
-
{
|
|
296
|
-
await this.processApiUpdates(filePath, fileType);
|
|
297
|
-
}
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
case "unlink": {
|
|
303
|
-
if (filePath in this.apiFiles) {
|
|
304
|
-
const api = this.apiFiles[filePath];
|
|
305
|
-
delete this.apiFiles[filePath];
|
|
306
|
-
if (!api || !this.sourceTracker)
|
|
307
|
-
break;
|
|
308
|
-
const scopeId = await this.sourceTracker.deleteApi({
|
|
309
|
-
pageName: api.pageName,
|
|
310
|
-
apiName: api.apiPb.metadata.name,
|
|
311
|
-
});
|
|
312
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
313
|
-
await this.writeChanges(changes);
|
|
314
|
-
this.emit("apiManualDelete", {
|
|
315
|
-
api: {
|
|
316
|
-
id: getClientApiId(api.apiPb.metadata.name, api.pageName),
|
|
317
|
-
apiName: api.apiPb.metadata.name,
|
|
318
|
-
// TODO(saksham): get pagename more defensively
|
|
319
|
-
pageName: getPageName(filePath),
|
|
320
|
-
scopeId,
|
|
321
|
-
},
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
if (filePath in this.tsFiles) {
|
|
325
|
-
delete this.tsFiles[filePath];
|
|
326
|
-
this.sourceTracker?.removeFile(filePath);
|
|
327
|
-
this.emit("deletePage", filePath);
|
|
328
|
-
}
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
watcher.on("all", async (event, filePath) => {
|
|
334
|
-
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
335
|
-
switch (fileType?.type) {
|
|
336
|
-
case "tsx":
|
|
337
|
-
case "scope":
|
|
338
|
-
// these can be awaited to ensure sequential execution
|
|
339
|
-
this.fsOperationQueue.enqueue(async () => {
|
|
340
|
-
return await handleFileChange(event, filePath);
|
|
341
|
-
});
|
|
342
|
-
break;
|
|
343
|
-
default:
|
|
344
|
-
// some files including APIs cannot be awaited currently
|
|
345
|
-
this.fsOperationQueue.enqueue(async () => {
|
|
346
|
-
void handleFileChange(event, filePath);
|
|
347
|
-
});
|
|
216
|
+
this.updateInternalApiData(content, path);
|
|
348
217
|
}
|
|
349
218
|
});
|
|
350
|
-
|
|
351
|
-
getApiFiles() {
|
|
352
|
-
return this.formatApisToClientApis(this.apiFiles);
|
|
353
|
-
}
|
|
354
|
-
getTsFilePaths() {
|
|
355
|
-
return Object.keys(this.tsFiles);
|
|
356
|
-
}
|
|
357
|
-
getSourceTracker() {
|
|
358
|
-
return this.sourceTracker;
|
|
359
|
-
}
|
|
360
|
-
async writeFile(path, content, kind) {
|
|
361
|
-
if (kind === "ts") {
|
|
362
|
-
// happens eagerly regardless of error - possible desync
|
|
363
|
-
this.tsFiles[path] = content;
|
|
364
|
-
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
365
|
-
await this.fsOperationQueue.enqueue(async () => {
|
|
366
|
-
await fs.writeFile(path, content);
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
else if (kind === "api") {
|
|
370
|
-
const currentApiFile = this.apiFiles[path];
|
|
371
|
-
const apiPb = yaml.parse(content);
|
|
372
|
-
// Client APIs have id, but server APIs don't
|
|
373
|
-
if (apiPb.metadata.id) {
|
|
374
|
-
delete apiPb.metadata.id;
|
|
375
|
-
}
|
|
376
|
-
const stepPathMap = currentApiFile?.stepPathMap ?? {};
|
|
377
|
-
this.updateApi({ api: apiPb, stepPathMap }, path);
|
|
378
|
-
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
379
|
-
await this.fsOperationQueue.enqueue(async () => {
|
|
380
|
-
await writeApiFiles({ apiPb }, "api", path.split("/").slice(0, -1).join("/"), false, [], [], { extractLargeSourceFiles: true, minLinesForExtraction: 1 }, new Set(Object.keys(this.apiFiles)), stepPathMap);
|
|
381
|
-
// stepPathMap will be generated after the write when the file doesn't exist
|
|
382
|
-
if (this.apiFiles[path]) {
|
|
383
|
-
this.apiFiles[path].stepPathMap = stepPathMap;
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
readFile(path) {
|
|
389
|
-
return this.tsFiles[path];
|
|
219
|
+
watcher.on("all", this.handleFileChange);
|
|
390
220
|
}
|
|
391
221
|
initializeSourceTracker() {
|
|
392
222
|
this.sourceTracker = new SourceTracker(this._tracer);
|
|
@@ -400,49 +230,56 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
400
230
|
}
|
|
401
231
|
return this.sourceTracker.handleNonVisualChangeByDeletingIds(path, content);
|
|
402
232
|
}
|
|
403
|
-
async getFileWithIds(
|
|
233
|
+
async getFileWithIds(filePath) {
|
|
404
234
|
const logger = getLogger();
|
|
405
235
|
if (!this.sourceTracker) {
|
|
406
236
|
throw new Error("Source tracker not initialized");
|
|
407
237
|
}
|
|
408
238
|
const files = this.sourceTracker.getCurrentFiles();
|
|
409
|
-
if (!files[
|
|
410
|
-
throw new Error("File not found in source tracker " +
|
|
239
|
+
if (!files[filePath]) {
|
|
240
|
+
throw new Error("File not found in source tracker " + filePath);
|
|
411
241
|
}
|
|
412
|
-
const ast = files[
|
|
242
|
+
const ast = files[filePath].ast;
|
|
413
243
|
if (!ast) {
|
|
414
|
-
throw new Error("No AST found for file in source tracker " +
|
|
244
|
+
throw new Error("No AST found for file in source tracker " + filePath);
|
|
415
245
|
}
|
|
416
246
|
const clonedAst = await transformFromAstAsync(ast, undefined, {
|
|
417
247
|
ast: true,
|
|
418
248
|
});
|
|
419
249
|
if (!clonedAst?.ast) {
|
|
420
|
-
throw new Error("Failed to clone AST for file " +
|
|
250
|
+
throw new Error("Failed to clone AST for file " + filePath);
|
|
421
251
|
}
|
|
252
|
+
const componentsManager = ComponentsManager.getInstance();
|
|
422
253
|
const processedTransactions = this.getProcessedTransactionsWithNonce();
|
|
423
254
|
const customImports = new Set();
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
255
|
+
traverse(clonedAst.ast, {
|
|
256
|
+
Program: {
|
|
257
|
+
exit(nodePath) {
|
|
258
|
+
if (isCustomBuildEnabled()) {
|
|
259
|
+
addLegacyCustomComponentVariables(nodePath, customImports);
|
|
260
|
+
}
|
|
261
|
+
// we want to inject the component name into the file so we can use it for hot reloading purposes
|
|
262
|
+
const name = componentsManager.getComponentName(filePath);
|
|
263
|
+
if (name) {
|
|
264
|
+
nodePath.pushContainer("body", t.variableDeclaration("const", [
|
|
265
|
+
t.variableDeclarator(t.identifier("componentName"), t.stringLiteral(name)),
|
|
266
|
+
]));
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
ImportDeclaration(path) {
|
|
271
|
+
if (isCustomBuildEnabled()) {
|
|
428
272
|
// when we see a custom component, we want to track the import and modify it to point
|
|
429
273
|
// to the built source
|
|
430
274
|
modifyLegacyCustomComponentImports(path, customImports);
|
|
431
|
-
}
|
|
432
|
-
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
JSXElement(path) {
|
|
278
|
+
if (isCustomBuildEnabled()) {
|
|
433
279
|
// based on the imports we see, we want to modify the JSXElement that uses the custom component
|
|
434
280
|
modifyLegacyCustomComponentElements(path, customImports);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// we want to write the wrapped custom elements as local variables to the file
|
|
438
|
-
exit(path) {
|
|
439
|
-
addLegacyCustomComponentVariables(path, customImports);
|
|
440
|
-
},
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
traverse(clonedAst.ast, {
|
|
445
|
-
...customBuildTraversal,
|
|
281
|
+
}
|
|
282
|
+
},
|
|
446
283
|
ReturnStatement(path) {
|
|
447
284
|
const argument = path.get("argument");
|
|
448
285
|
if (!argument.isJSXElement()) {
|
|
@@ -488,6 +325,246 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
488
325
|
return null;
|
|
489
326
|
}
|
|
490
327
|
}
|
|
328
|
+
enableOperationsQueue() {
|
|
329
|
+
this.operationProcessor.enable();
|
|
330
|
+
}
|
|
331
|
+
disableOperationsQueue() {
|
|
332
|
+
this.operationProcessor.disable();
|
|
333
|
+
}
|
|
334
|
+
async flushOperations() {
|
|
335
|
+
await this.operationProcessor.flush();
|
|
336
|
+
}
|
|
337
|
+
// MARK: file change handling
|
|
338
|
+
handleFileChange = async (event, filePath) => {
|
|
339
|
+
const logger = getLogger();
|
|
340
|
+
logger.info(`File changed: ${filePath}, event: ${event}`);
|
|
341
|
+
const rootPath = this.rootDir;
|
|
342
|
+
if (!rootPath) {
|
|
343
|
+
throw new Error("Root directory not set");
|
|
344
|
+
}
|
|
345
|
+
// Skip directory events
|
|
346
|
+
if (event === "addDir" || event === "unlinkDir") {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const routePath = path.join(rootPath, ROUTES_FILE);
|
|
350
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
351
|
+
// Only handle files we care about and that are in our root path
|
|
352
|
+
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
// Queue the operation based on the event type
|
|
356
|
+
switch (event) {
|
|
357
|
+
case "add": {
|
|
358
|
+
const data = await readFile(filePath);
|
|
359
|
+
if (typeof data !== "string")
|
|
360
|
+
return;
|
|
361
|
+
const isPage = isPageFilePath(filePath);
|
|
362
|
+
if (isPage) {
|
|
363
|
+
void this.operationProcessor.addOperation({
|
|
364
|
+
metadata: {
|
|
365
|
+
filePath,
|
|
366
|
+
},
|
|
367
|
+
execute: async () => {
|
|
368
|
+
const file = await readFile(filePath);
|
|
369
|
+
if (!file) {
|
|
370
|
+
logger.error(`Failed to read file: ${filePath}`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (!(filePath in this.tsFiles)) {
|
|
374
|
+
this.tsFiles[filePath] = file;
|
|
375
|
+
this.handleNonVisualChangeByDeletingIds(filePath, file);
|
|
376
|
+
this.emit("addPage", filePath);
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
void this.operationProcessor.addOperation({
|
|
383
|
+
metadata: {
|
|
384
|
+
filePath,
|
|
385
|
+
},
|
|
386
|
+
execute: async () => {
|
|
387
|
+
switch (fileType.type) {
|
|
388
|
+
case "api":
|
|
389
|
+
case "python-api-step":
|
|
390
|
+
case "js-api-step": {
|
|
391
|
+
await this.processApiFileUpdates(filePath, fileType);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
case "change": {
|
|
401
|
+
if (filePath === routePath) {
|
|
402
|
+
void this.operationProcessor.addOperation({
|
|
403
|
+
metadata: {
|
|
404
|
+
filePath,
|
|
405
|
+
},
|
|
406
|
+
priority: true,
|
|
407
|
+
execute: async () => {
|
|
408
|
+
try {
|
|
409
|
+
const data = JSON.parse((await readFile(filePath)) ?? "{}");
|
|
410
|
+
if (!isEqual(this.routes, data))
|
|
411
|
+
this.routes = data;
|
|
412
|
+
// this.addRoute assigns this.routes itself, causing this.routes === data
|
|
413
|
+
// but we still want to emit the event to HMR root.tsx
|
|
414
|
+
this.emit("routesChanged", this.routes);
|
|
415
|
+
}
|
|
416
|
+
catch (e) {
|
|
417
|
+
logger.error("Error parsing routes file", getErrorMeta(e));
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
void this.operationProcessor.addOperation({
|
|
425
|
+
metadata: {
|
|
426
|
+
filePath,
|
|
427
|
+
},
|
|
428
|
+
execute: async () => {
|
|
429
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
430
|
+
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const data = await readFile(filePath);
|
|
434
|
+
if (typeof data !== "string")
|
|
435
|
+
return;
|
|
436
|
+
switch (fileType.type) {
|
|
437
|
+
case "tsx":
|
|
438
|
+
case "scope":
|
|
439
|
+
{
|
|
440
|
+
if (!(filePath in this.tsFiles &&
|
|
441
|
+
this.tsFiles[filePath] === data)) {
|
|
442
|
+
logger.info(`File changed: ${filePath} updating AST tracker`);
|
|
443
|
+
this.tsFiles[filePath] = data;
|
|
444
|
+
// only update the source tracker if the file is different
|
|
445
|
+
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
446
|
+
this.emit("fileChanged", filePath, data, true);
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
logger.info(`File unchanged from last tracked state: ${filePath}`);
|
|
450
|
+
this.emit("fileChanged", filePath, data, false);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
break;
|
|
454
|
+
case "api":
|
|
455
|
+
case "python-api-step":
|
|
456
|
+
case "js-api-step":
|
|
457
|
+
{
|
|
458
|
+
await this.processApiFileUpdates(filePath, fileType);
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case "unlink": {
|
|
468
|
+
if (filePath in this.tsFiles) {
|
|
469
|
+
void this.operationProcessor.addOperation({
|
|
470
|
+
metadata: {
|
|
471
|
+
filePath,
|
|
472
|
+
},
|
|
473
|
+
execute: async () => {
|
|
474
|
+
await this.deleteTsFile(filePath);
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
else if (filePath in this.apiFiles) {
|
|
479
|
+
void this.operationProcessor.addOperation({
|
|
480
|
+
metadata: {
|
|
481
|
+
filePath,
|
|
482
|
+
},
|
|
483
|
+
execute: async () => {
|
|
484
|
+
await this.removeApiData(filePath);
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
async deleteTsFile(filePath) {
|
|
493
|
+
delete this.tsFiles[filePath];
|
|
494
|
+
this.sourceTracker?.removeFile(filePath);
|
|
495
|
+
if (isPageFilePath(filePath)) {
|
|
496
|
+
this.emit("deletePage", filePath);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
getTsFilePaths() {
|
|
500
|
+
return Object.keys(this.tsFiles);
|
|
501
|
+
}
|
|
502
|
+
getSourceTracker() {
|
|
503
|
+
return this.sourceTracker;
|
|
504
|
+
}
|
|
505
|
+
// MARK: fs read/write
|
|
506
|
+
async writeFile(path, content, kind) {
|
|
507
|
+
switch (kind) {
|
|
508
|
+
case "ts": {
|
|
509
|
+
// happens eagerly regardless of error - possible desync
|
|
510
|
+
this.tsFiles[path] = content;
|
|
511
|
+
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
512
|
+
await this.fsOperationQueue.enqueue(async () => {
|
|
513
|
+
await fs.writeFile(path, content);
|
|
514
|
+
});
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case "api": {
|
|
518
|
+
const currentApiFile = this.apiFiles[path];
|
|
519
|
+
const apiPb = yaml.parse(content);
|
|
520
|
+
// Client APIs have id, but server APIs don't
|
|
521
|
+
if (apiPb.metadata.id) {
|
|
522
|
+
delete apiPb.metadata.id;
|
|
523
|
+
}
|
|
524
|
+
const stepPathMap = currentApiFile?.stepPathMap ?? {};
|
|
525
|
+
this.updateInternalApiData({ api: apiPb, stepPathMap }, path);
|
|
526
|
+
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
527
|
+
await this.fsOperationQueue.enqueue(async () => {
|
|
528
|
+
await writeApiFiles({ apiPb }, "api", path.split("/").slice(0, -1).join("/"), false, [], [], { extractLargeSourceFiles: true, minLinesForExtraction: 1 }, new Set(Object.keys(this.apiFiles)), stepPathMap);
|
|
529
|
+
// stepPathMap will be generated after the write when the file doesn't exist
|
|
530
|
+
if (this.apiFiles[path]) {
|
|
531
|
+
this.apiFiles[path].stepPathMap = stepPathMap;
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
default: {
|
|
537
|
+
// TODO(george): as an optimization, we could update the memory snapshot of the file that SyncService is holding
|
|
538
|
+
await this.fsOperationQueue.enqueue(async () => {
|
|
539
|
+
await fs.writeFile(path, content);
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
readFile(path) {
|
|
545
|
+
return this.tsFiles[path];
|
|
546
|
+
}
|
|
547
|
+
async addRoute(route, filePath) {
|
|
548
|
+
if (!this.rootDir) {
|
|
549
|
+
throw new Error("Root directory not set");
|
|
550
|
+
}
|
|
551
|
+
this.routes[route] = { file: this.getRelativeRoutePath(filePath) };
|
|
552
|
+
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
553
|
+
}
|
|
554
|
+
async removeRoute(filePath) {
|
|
555
|
+
if (!this.rootDir) {
|
|
556
|
+
throw new Error("Root directory not set");
|
|
557
|
+
}
|
|
558
|
+
const relativeFilePath = this.getRelativeRoutePath(filePath);
|
|
559
|
+
this.routes = Object.fromEntries(Object.entries(this.routes).filter(([_, value]) => value.file !== relativeFilePath));
|
|
560
|
+
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
561
|
+
}
|
|
562
|
+
async writeChanges(changes, callback) {
|
|
563
|
+
return Promise.all(changes.map(async ({ fileName, source, kind }) => {
|
|
564
|
+
await this.writeFile(fileName, source, kind);
|
|
565
|
+
return callback?.(fileName, source);
|
|
566
|
+
}));
|
|
567
|
+
}
|
|
491
568
|
getLocalBindingEntities(path) {
|
|
492
569
|
const logger = getLogger();
|
|
493
570
|
const files = this.sourceTracker?.getCurrentFiles();
|
|
@@ -515,8 +592,36 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
515
592
|
});
|
|
516
593
|
return Array.from(localBindingEntities);
|
|
517
594
|
}
|
|
518
|
-
|
|
519
|
-
|
|
595
|
+
getPageRoots(filePath) {
|
|
596
|
+
const scopeFilePath = path.join(path.dirname(filePath), "index.tsx");
|
|
597
|
+
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
598
|
+
if (!currentFile) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
return getPageRoots(filePath, currentFile);
|
|
602
|
+
}
|
|
603
|
+
getScope(filePath) {
|
|
604
|
+
// get the scope.ts file in the same directory as path
|
|
605
|
+
const scopeFilePath = path.join(path.dirname(filePath), SCOPE_FILE);
|
|
606
|
+
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
607
|
+
if (!currentFile) {
|
|
608
|
+
console.log("File not found", scopeFilePath);
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
const scope = getScope(scopeFilePath, currentFile);
|
|
612
|
+
return scope;
|
|
613
|
+
}
|
|
614
|
+
getRoutes() {
|
|
615
|
+
const routes = [];
|
|
616
|
+
for (const [path, { file }] of Object.entries(this.routes)) {
|
|
617
|
+
routes.push({
|
|
618
|
+
path,
|
|
619
|
+
component: file,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
return routes;
|
|
623
|
+
}
|
|
624
|
+
// MARK: transaction handling
|
|
520
625
|
flushTransactions = () => {
|
|
521
626
|
// TODO do something more sophisticated than this.
|
|
522
627
|
this.processedTransactions = this.processedTransactions.slice(-20);
|
|
@@ -529,42 +634,92 @@ export class FileSyncManager extends TracedEventEmitter {
|
|
|
529
634
|
}
|
|
530
635
|
this.pendingTransactions.add(transactionId);
|
|
531
636
|
};
|
|
532
|
-
transactionNonce = Date.now();
|
|
533
637
|
// Until we have a sophisticated way to track ALL operations, including non-UI initiated ops, we will use a nonce
|
|
534
638
|
// TODO https://github.com/superblocksteam/superblocks/pull/11788
|
|
535
639
|
getProcessedTransactionsWithNonce() {
|
|
536
640
|
const nonce = `t-${this.transactionNonce++}`;
|
|
537
641
|
return this.processedTransactions.concat(nonce);
|
|
538
642
|
}
|
|
643
|
+
// MARK: editor operations
|
|
539
644
|
handleCreatePage = async (payload) => {
|
|
540
|
-
|
|
645
|
+
this.trackTransaction(payload.transaction?.id);
|
|
646
|
+
const { name, route, navigateToRoute, routeTestParams } = payload;
|
|
541
647
|
if (!this.rootDir) {
|
|
542
648
|
throw new Error("Root directory not set");
|
|
543
649
|
}
|
|
650
|
+
if (!route) {
|
|
651
|
+
throw new Error("Route is required when creating a page");
|
|
652
|
+
}
|
|
544
653
|
const pagePath = getPageFolder(this.rootDir, name);
|
|
545
654
|
const pageIndexPath = path.join(pagePath, "index.tsx");
|
|
546
|
-
const
|
|
655
|
+
const scopePath = path.join(pagePath, "scope.ts");
|
|
656
|
+
const pageContent = /*js*/ `import {
|
|
657
|
+
SbPage,
|
|
658
|
+
Dim,
|
|
659
|
+
SbSection,
|
|
660
|
+
SbColumn,
|
|
661
|
+
registerPage,
|
|
662
|
+
} from "@superblocksteam/library";
|
|
663
|
+
import { ${name}, ${name}Scope } from "./scope";
|
|
664
|
+
|
|
547
665
|
function Page() {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
666
|
+
const {} = ${name};
|
|
667
|
+
return (
|
|
668
|
+
<SbPage name="${name}" height={Dim.fill()} width={Dim.fill()}>
|
|
669
|
+
<SbSection height={Dim.fill()}>
|
|
670
|
+
<SbColumn width={Dim.fill()}></SbColumn>
|
|
671
|
+
</SbSection>
|
|
672
|
+
</SbPage>
|
|
673
|
+
);
|
|
551
674
|
}
|
|
552
675
|
|
|
553
|
-
export default registerPage(Page,
|
|
676
|
+
export default registerPage(Page, ${name}Scope);
|
|
677
|
+
`;
|
|
678
|
+
const scopeContent = /*js*/ `import { createSbScope } from "@superblocksteam/library";
|
|
679
|
+
|
|
680
|
+
export const ${name}Scope = createSbScope<{}>(({ entities }) => ({}), {
|
|
681
|
+
name: "${name}",
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
export const ${name} = ${name}Scope.entities;
|
|
554
685
|
`;
|
|
686
|
+
if (navigateToRoute) {
|
|
687
|
+
this.routeChangesQueue.push({
|
|
688
|
+
route: route,
|
|
689
|
+
routeTestParams: routeTestParams,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
this.watcher?.unwatch(pagePath);
|
|
555
693
|
await fs.mkdir(pagePath, { recursive: true });
|
|
556
694
|
await this.writeFile(pageIndexPath, pageContent, "ts");
|
|
695
|
+
await this.writeFile(scopePath, scopeContent, "ts");
|
|
557
696
|
await this.handleNonVisualChangeByDeletingIds(pageIndexPath, pageContent);
|
|
697
|
+
await this.handleNonVisualChangeByDeletingIds(scopePath, scopeContent);
|
|
698
|
+
this.watcher?.add(pagePath);
|
|
699
|
+
await this.addRoute(route, pageIndexPath);
|
|
558
700
|
this.emit("addPage", pageIndexPath);
|
|
559
701
|
};
|
|
702
|
+
handleDeletePage = async (payload) => {
|
|
703
|
+
const { name } = payload;
|
|
704
|
+
if (!this.rootDir) {
|
|
705
|
+
throw new Error("Root directory not set");
|
|
706
|
+
}
|
|
707
|
+
const pagePath = getPageFolder(this.rootDir, name);
|
|
708
|
+
await fs.rm(pagePath, { recursive: true, force: true });
|
|
709
|
+
await this.removeRoute(name);
|
|
710
|
+
this.emit("deletePage", pagePath);
|
|
711
|
+
};
|
|
712
|
+
get routeChange() {
|
|
713
|
+
return this.routeChangesQueue.shift();
|
|
714
|
+
}
|
|
560
715
|
handleReparent = async (payload, writeFile = true) => {
|
|
561
716
|
const { from, to, changedProps, transaction } = payload;
|
|
562
717
|
this.trackTransaction(transaction?.id);
|
|
563
|
-
|
|
718
|
+
this.sourceTracker?.setProperties({
|
|
564
719
|
source: from.source,
|
|
565
720
|
changes: changedProps ?? {},
|
|
566
721
|
});
|
|
567
|
-
|
|
722
|
+
this.sourceTracker?.moveElement({
|
|
568
723
|
from,
|
|
569
724
|
to,
|
|
570
725
|
});
|
|
@@ -578,7 +733,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
578
733
|
};
|
|
579
734
|
handleCreateComponent = async (payload, writeFile = true) => {
|
|
580
735
|
this.trackTransaction(payload.transaction?.id);
|
|
581
|
-
const sourceId =
|
|
736
|
+
const sourceId = this.sourceTracker?.addElement(payload);
|
|
582
737
|
if (writeFile) {
|
|
583
738
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
584
739
|
await this.writeChanges(changes ?? [], (fileName) => {
|
|
@@ -592,7 +747,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
592
747
|
const { elements, transaction } = payload;
|
|
593
748
|
this.trackTransaction(transaction?.id);
|
|
594
749
|
for (const element of elements) {
|
|
595
|
-
|
|
750
|
+
this.sourceTracker?.deleteElement({
|
|
596
751
|
source: element.source,
|
|
597
752
|
scopeName: element.scopeName,
|
|
598
753
|
});
|
|
@@ -608,7 +763,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
608
763
|
handleSetProperty = async (payload, writeFile = true) => {
|
|
609
764
|
const { element: { source }, property, value, transaction, } = payload;
|
|
610
765
|
this.trackTransaction(transaction?.id);
|
|
611
|
-
|
|
766
|
+
this.sourceTracker?.setProperty({
|
|
612
767
|
source,
|
|
613
768
|
property,
|
|
614
769
|
info: value,
|
|
@@ -623,7 +778,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
623
778
|
handleSetProperties = async (payload, writeFile = true) => {
|
|
624
779
|
const { element: { source }, properties, transaction, } = payload;
|
|
625
780
|
this.trackTransaction(transaction?.id);
|
|
626
|
-
|
|
781
|
+
this.sourceTracker?.setProperties({
|
|
627
782
|
source,
|
|
628
783
|
changes: properties,
|
|
629
784
|
});
|
|
@@ -679,11 +834,152 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
679
834
|
this.flushTransactions();
|
|
680
835
|
return returnValues;
|
|
681
836
|
};
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
837
|
+
// MARK: entity operations
|
|
838
|
+
handleAddEntity = async (payload) => {
|
|
839
|
+
this.sourceTracker?.addEntity({
|
|
840
|
+
scopeId: payload.scopeId,
|
|
841
|
+
entity: {
|
|
842
|
+
type: payload.type,
|
|
843
|
+
name: payload.name,
|
|
844
|
+
attributes: payload.attributes,
|
|
845
|
+
},
|
|
846
|
+
});
|
|
847
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
848
|
+
await this.writeChanges(changes, (fileName) => {
|
|
849
|
+
this.emit("addEntity", fileName, payload);
|
|
850
|
+
});
|
|
851
|
+
};
|
|
852
|
+
handleUpdateEntity = async (payload) => {
|
|
853
|
+
this.sourceTracker?.updateEntity({
|
|
854
|
+
entityId: payload.entityId,
|
|
855
|
+
updates: payload.updates,
|
|
856
|
+
});
|
|
857
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
858
|
+
await this.writeChanges(changes, (fileName) => {
|
|
859
|
+
this.emit("updateEntity", fileName, payload);
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
handleDeleteEntity = async (payload) => {
|
|
863
|
+
const deletedEntityName = this.sourceTracker?.deleteEntity({
|
|
864
|
+
entityId: payload.entityId,
|
|
865
|
+
});
|
|
866
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
867
|
+
await this.writeChanges(changes, (fileName) => {
|
|
868
|
+
if (deletedEntityName) {
|
|
869
|
+
this.emit("deleteEntity", fileName, deletedEntityName);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
};
|
|
873
|
+
handleUpdateTheme = async (payload) => {
|
|
874
|
+
const { theme } = payload;
|
|
875
|
+
if (!this.rootDir) {
|
|
876
|
+
throw new Error("Root directory not set");
|
|
877
|
+
}
|
|
878
|
+
const filePath = path.join(this.rootDir, APP_THEME_FILE_NAME);
|
|
879
|
+
this.sourceTracker?.updateTheme({
|
|
880
|
+
themeFilePath: filePath,
|
|
881
|
+
theme,
|
|
882
|
+
});
|
|
883
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
884
|
+
await this.writeChanges(changes);
|
|
885
|
+
};
|
|
886
|
+
// MARK: rename operations
|
|
887
|
+
handleRenameElement = async (payload) => {
|
|
888
|
+
if (payload.kind === "component") {
|
|
889
|
+
return this.handleRenameComponent(payload);
|
|
890
|
+
}
|
|
891
|
+
else if (payload.kind === "entity") {
|
|
892
|
+
return this.handleRenameEntity(payload);
|
|
893
|
+
}
|
|
894
|
+
else if (payload.kind === "page") {
|
|
895
|
+
return this.handleRenamePage(payload);
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
handleRenameComponent = async (payload) => {
|
|
899
|
+
const { elementId, newName, oldName, scopeName } = payload;
|
|
900
|
+
await this.sourceTracker?.renameComponent({
|
|
901
|
+
widgetSourceId: elementId,
|
|
902
|
+
oldName,
|
|
903
|
+
newName,
|
|
904
|
+
scopeName,
|
|
905
|
+
});
|
|
906
|
+
await this.renameIdentifierInApis({
|
|
907
|
+
elementId,
|
|
908
|
+
oldName,
|
|
909
|
+
newName,
|
|
910
|
+
});
|
|
911
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
912
|
+
await this.writeChanges(changes, (fileName) => {
|
|
913
|
+
this.emit("renameComponent", fileName);
|
|
914
|
+
});
|
|
915
|
+
};
|
|
916
|
+
handleRenameEntity = async (payload) => {
|
|
917
|
+
const { elementId, newName, oldName, scopeName } = payload;
|
|
918
|
+
this.sourceTracker?.renameEntity({
|
|
919
|
+
entityId: elementId,
|
|
920
|
+
oldName,
|
|
921
|
+
newName,
|
|
922
|
+
scopeName,
|
|
923
|
+
});
|
|
924
|
+
await this.renameIdentifierInApis(payload);
|
|
925
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
926
|
+
await this.writeChanges(changes, (fileName) => {
|
|
927
|
+
this.emit("renameEntity", fileName);
|
|
928
|
+
});
|
|
929
|
+
};
|
|
930
|
+
handleRenamePage = async (payload) => {
|
|
931
|
+
const { newName, oldName } = payload;
|
|
932
|
+
if (!this.rootDir) {
|
|
933
|
+
throw new Error("Root directory not set");
|
|
934
|
+
}
|
|
935
|
+
const newPageFolder = getPageFolder(this.rootDir, newName);
|
|
936
|
+
const newIndexFilePath = path.join(newPageFolder, "index.tsx");
|
|
937
|
+
const oldIndexFilePath = path.join(this.rootDir, "pages", oldName, "index.tsx");
|
|
938
|
+
const oldPageFolder = getPageFolder(this.rootDir, oldName);
|
|
939
|
+
this.watcher?.unwatch(newPageFolder);
|
|
940
|
+
this.watcher?.unwatch(oldPageFolder);
|
|
941
|
+
const existingRoute = Object.keys(this.routes).find((route) => this.routes[route]?.file ===
|
|
942
|
+
this.getRelativeRoutePath(oldIndexFilePath));
|
|
943
|
+
if (!existingRoute) {
|
|
944
|
+
throw new Error(`Route for ${oldName} not found`);
|
|
945
|
+
}
|
|
946
|
+
// Write the name attribute to the page file
|
|
947
|
+
await this.sourceTracker?.renamePage({
|
|
948
|
+
newName,
|
|
949
|
+
filePath: oldIndexFilePath,
|
|
950
|
+
});
|
|
951
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
952
|
+
await this.writeChanges(changes);
|
|
953
|
+
// A rename of a folder is an unlink followed by an add, so the file watcher will take over initializing this
|
|
954
|
+
// "new" page
|
|
955
|
+
await fs.rename(oldPageFolder, newPageFolder);
|
|
956
|
+
// Now we just clean up the routes
|
|
957
|
+
await this.removeRoute(oldIndexFilePath);
|
|
958
|
+
await this.addRoute(existingRoute, newIndexFilePath);
|
|
959
|
+
const newIndexFile = await readFile(newIndexFilePath);
|
|
960
|
+
if (!newIndexFile) {
|
|
961
|
+
throw new Error(`New index file ${newIndexFilePath} not found`);
|
|
962
|
+
}
|
|
963
|
+
this.tsFiles[newIndexFilePath] = newIndexFile;
|
|
964
|
+
await this.handleNonVisualChangeByDeletingIds(newIndexFilePath, newIndexFile);
|
|
965
|
+
this.emit("renamePage", newIndexFilePath);
|
|
966
|
+
// Re-add the watcher
|
|
967
|
+
this.watcher?.add(newPageFolder);
|
|
968
|
+
this.watcher?.add(oldPageFolder);
|
|
969
|
+
};
|
|
970
|
+
getRelativeRoutePath(filePath) {
|
|
971
|
+
if (!this.rootDir) {
|
|
972
|
+
throw new Error("Root directory not set");
|
|
973
|
+
}
|
|
974
|
+
// no leading slash
|
|
975
|
+
return path.relative(path.join(this.rootDir, PAGES_DIRECTORY), filePath);
|
|
976
|
+
}
|
|
977
|
+
// MARK: API operations
|
|
978
|
+
handleUpdateApi = async (payload) => {
|
|
979
|
+
const { api } = payload;
|
|
980
|
+
if (!this.sourceTracker) {
|
|
981
|
+
throw new Error("Source tracker not initialized");
|
|
982
|
+
}
|
|
687
983
|
if (!this.rootDir) {
|
|
688
984
|
throw new Error("Root directory not set");
|
|
689
985
|
}
|
|
@@ -694,24 +990,24 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
694
990
|
if (!apiName) {
|
|
695
991
|
throw new Error("API name is not set");
|
|
696
992
|
}
|
|
697
|
-
const
|
|
698
|
-
const
|
|
699
|
-
const isNewApi = !this.getApiFiles()[
|
|
993
|
+
const apiFilePath = getApiFilePath(this.rootDir, api.pageName, apiName);
|
|
994
|
+
const apiDir = path.dirname(apiFilePath);
|
|
995
|
+
const isNewApi = !this.getApiFiles()[apiFilePath];
|
|
700
996
|
try {
|
|
701
997
|
const stats = await fs.stat(apiDir);
|
|
702
998
|
if (!stats.isDirectory()) {
|
|
703
|
-
await fs.mkdir(apiDir, { recursive: true });
|
|
999
|
+
await this.fsOperationQueue.enqueue(async () => await fs.mkdir(apiDir, { recursive: true }));
|
|
704
1000
|
}
|
|
705
1001
|
}
|
|
706
1002
|
catch {
|
|
707
|
-
await fs.mkdir(apiDir, { recursive: true });
|
|
1003
|
+
await this.fsOperationQueue.enqueue(async () => await fs.mkdir(apiDir, { recursive: true }));
|
|
708
1004
|
}
|
|
709
|
-
await this.writeFile(
|
|
1005
|
+
await this.writeFile(apiFilePath, yaml.stringify(api.apiPb), "api");
|
|
710
1006
|
const generationNumber = this.generationNumberSequence.next();
|
|
711
1007
|
const apiDef = this.createClientApi(api);
|
|
712
1008
|
let scopeId = "";
|
|
713
1009
|
if (isNewApi) {
|
|
714
|
-
scopeId = await this.
|
|
1010
|
+
scopeId = await this.addApiToScope(api);
|
|
715
1011
|
}
|
|
716
1012
|
else {
|
|
717
1013
|
const scopeDef = this.sourceTracker.getScopeDefinitionForPage(api.pageName);
|
|
@@ -720,20 +1016,6 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
720
1016
|
this.emit("apiUpdate", { api: apiDef, scopeId });
|
|
721
1017
|
return { api: apiDef, scopeId, generationNumber };
|
|
722
1018
|
};
|
|
723
|
-
createScopedApi = async (api) => {
|
|
724
|
-
if (!this.sourceTracker) {
|
|
725
|
-
throw new Error("Source tracker not initialized");
|
|
726
|
-
}
|
|
727
|
-
// We want to add the API entity to our scope, but we do not want to emit entity events, because
|
|
728
|
-
// the API update event handles this particular side effect.
|
|
729
|
-
const scopeId = await this.sourceTracker.addApi({
|
|
730
|
-
pageName: api.pageName,
|
|
731
|
-
apiName: api.apiPb.metadata.name,
|
|
732
|
-
});
|
|
733
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
734
|
-
await this.writeChanges(changes);
|
|
735
|
-
return scopeId;
|
|
736
|
-
};
|
|
737
1019
|
handleDeleteApi = async (payload) => {
|
|
738
1020
|
const logger = getLogger();
|
|
739
1021
|
const { apis } = payload;
|
|
@@ -741,40 +1023,34 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
741
1023
|
throw new Error("Root directory not set");
|
|
742
1024
|
}
|
|
743
1025
|
const rootDir = this.rootDir;
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
if (stats.isDirectory()) {
|
|
757
|
-
await fs.rmdir(apiDir, { recursive: true });
|
|
758
|
-
}
|
|
759
|
-
delete this.apiFiles[apiFilePath];
|
|
760
|
-
const scopeId = await this.sourceTracker.deleteApi({
|
|
761
|
-
pageName,
|
|
762
|
-
apiName,
|
|
763
|
-
});
|
|
764
|
-
resolve({
|
|
765
|
-
apiName,
|
|
766
|
-
pageName,
|
|
767
|
-
scopeId,
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
catch (e) {
|
|
771
|
-
logger.warn(`Could not delete api ${apiFilePath} ${JSON.stringify(e)}`);
|
|
772
|
-
resolve(undefined);
|
|
1026
|
+
const deletedApis = [];
|
|
1027
|
+
for (const { apiName, pageName } of apis) {
|
|
1028
|
+
const apiFilePath = getApiFilePath(rootDir, pageName, apiName);
|
|
1029
|
+
const api = this.apiFiles[apiFilePath];
|
|
1030
|
+
if (!api || !this.sourceTracker) {
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const apiDir = path.dirname(apiFilePath);
|
|
1034
|
+
try {
|
|
1035
|
+
const stats = await fs.stat(apiDir);
|
|
1036
|
+
if (stats.isDirectory()) {
|
|
1037
|
+
await fs.rmdir(apiDir, { recursive: true });
|
|
773
1038
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1039
|
+
delete this.apiFiles[apiFilePath];
|
|
1040
|
+
const scopeId = this.sourceTracker.deleteApi({
|
|
1041
|
+
pageName,
|
|
1042
|
+
apiName,
|
|
1043
|
+
});
|
|
1044
|
+
deletedApis.push({
|
|
1045
|
+
apiName,
|
|
1046
|
+
pageName,
|
|
1047
|
+
scopeId,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
catch (e) {
|
|
1051
|
+
logger.warn(`Could not delete api ${apiFilePath} ${JSON.stringify(e)}`);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
778
1054
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
779
1055
|
await this.writeChanges(changes);
|
|
780
1056
|
this.emit("apiDelete", {
|
|
@@ -855,229 +1131,40 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
855
1131
|
// TODO: Should I delete here?
|
|
856
1132
|
}));
|
|
857
1133
|
};
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
type: payload.type,
|
|
863
|
-
name: payload.name,
|
|
864
|
-
attributes: payload.attributes,
|
|
865
|
-
},
|
|
866
|
-
});
|
|
867
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
868
|
-
await this.writeChanges(changes, (fileName) => {
|
|
869
|
-
this.emit("addEntity", fileName, payload);
|
|
870
|
-
});
|
|
871
|
-
};
|
|
872
|
-
handleUpdateEntity = async (payload) => {
|
|
873
|
-
await this.sourceTracker?.updateEntity({
|
|
874
|
-
entityId: payload.entityId,
|
|
875
|
-
updates: payload.updates,
|
|
876
|
-
});
|
|
877
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
878
|
-
await this.writeChanges(changes, (fileName) => {
|
|
879
|
-
this.emit("updateEntity", fileName, payload);
|
|
880
|
-
});
|
|
881
|
-
};
|
|
882
|
-
handleDeleteEntity = async (payload) => {
|
|
883
|
-
const deletedEntityName = await this.sourceTracker?.deleteEntity({
|
|
884
|
-
entityId: payload.entityId,
|
|
885
|
-
});
|
|
886
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
887
|
-
await this.writeChanges(changes, (fileName) => {
|
|
888
|
-
if (deletedEntityName) {
|
|
889
|
-
this.emit("deleteEntity", fileName, deletedEntityName);
|
|
890
|
-
}
|
|
891
|
-
});
|
|
892
|
-
};
|
|
893
|
-
handleUpdateTheme = async (payload) => {
|
|
894
|
-
const { theme } = payload;
|
|
895
|
-
if (!this.rootDir) {
|
|
896
|
-
throw new Error("Root directory not set");
|
|
1134
|
+
async removeApiData(filePath) {
|
|
1135
|
+
const api = this.apiFiles[filePath];
|
|
1136
|
+
if (!api) {
|
|
1137
|
+
return;
|
|
897
1138
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1139
|
+
delete this.apiFiles[filePath];
|
|
1140
|
+
this.sourceTracker?.deleteApi({
|
|
1141
|
+
pageName: api.pageName,
|
|
1142
|
+
apiName: api.apiPb.metadata.name,
|
|
902
1143
|
});
|
|
903
1144
|
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
904
1145
|
await this.writeChanges(changes);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
return this.handleRenamePage(payload);
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
handleRenameComponent = async (payload) => {
|
|
918
|
-
const { elementId, newName, oldName, scopeName } = payload;
|
|
919
|
-
await this.sourceTracker?.renameComponent({
|
|
920
|
-
widgetSourceId: elementId,
|
|
921
|
-
oldName,
|
|
922
|
-
newName,
|
|
923
|
-
scopeName,
|
|
924
|
-
});
|
|
925
|
-
await this.renameIdentifierInApis({
|
|
926
|
-
elementId,
|
|
927
|
-
oldName,
|
|
928
|
-
newName,
|
|
929
|
-
});
|
|
930
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
931
|
-
await this.writeChanges(changes, (fileName) => {
|
|
932
|
-
this.emit("renameComponent", fileName);
|
|
933
|
-
});
|
|
934
|
-
};
|
|
935
|
-
handleRenameEntity = async (payload) => {
|
|
936
|
-
const { elementId, newName, oldName, scopeName } = payload;
|
|
937
|
-
await this.sourceTracker?.renameEntity({
|
|
938
|
-
entityId: elementId,
|
|
939
|
-
oldName,
|
|
940
|
-
newName,
|
|
941
|
-
scopeName,
|
|
942
|
-
});
|
|
943
|
-
await this.renameIdentifierInApis(payload);
|
|
944
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
945
|
-
await this.writeChanges(changes, (fileName) => {
|
|
946
|
-
this.emit("renameEntity", fileName);
|
|
947
|
-
});
|
|
948
|
-
};
|
|
949
|
-
handleRenamePage = async (payload) => {
|
|
950
|
-
const { newName, oldName } = payload;
|
|
951
|
-
if (!this.rootDir) {
|
|
952
|
-
throw new Error("Root directory not set");
|
|
953
|
-
}
|
|
954
|
-
const newPageFolder = getPageFolder(this.rootDir, newName);
|
|
955
|
-
const newIndexFilePath = path.join(newPageFolder, "index.tsx");
|
|
956
|
-
const oldIndexFilePath = path.join(this.rootDir, "pages", oldName, "index.tsx");
|
|
957
|
-
const oldPageFolder = getPageFolder(this.rootDir, oldName);
|
|
958
|
-
this.watcher?.unwatch(newPageFolder);
|
|
959
|
-
this.watcher?.unwatch(oldPageFolder);
|
|
960
|
-
const existingRoute = Object.keys(this.routes).find((route) => this.routes[route]?.file ===
|
|
961
|
-
this.getRelativeRoutePath(oldIndexFilePath));
|
|
962
|
-
if (!existingRoute) {
|
|
963
|
-
throw new Error(`Route for ${oldName} not found`);
|
|
964
|
-
}
|
|
965
|
-
// Write the name attribute to the page file
|
|
966
|
-
await this.sourceTracker?.renamePage({
|
|
967
|
-
newName,
|
|
968
|
-
filePath: oldIndexFilePath,
|
|
1146
|
+
const scopeId = this.sourceTracker?.getScopeDefinitionForPage(api.pageName)?.id;
|
|
1147
|
+
this.emit("apiManualDelete", {
|
|
1148
|
+
api: {
|
|
1149
|
+
id: getClientApiId(api.apiPb.metadata.name, api.pageName),
|
|
1150
|
+
apiName: api.apiPb.metadata.name,
|
|
1151
|
+
// TODO(saksham): get pagename more defensively
|
|
1152
|
+
pageName: getPageName(filePath),
|
|
1153
|
+
scopeId,
|
|
1154
|
+
},
|
|
969
1155
|
});
|
|
970
|
-
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
971
|
-
await this.writeChanges(changes);
|
|
972
|
-
// A rename of a folder is an unlink followed by an add, so the file watcher will take over initializing this
|
|
973
|
-
// "new" page
|
|
974
|
-
await fs.rename(oldPageFolder, newPageFolder);
|
|
975
|
-
// Now we just clean up the routes
|
|
976
|
-
await this.removeRoute(oldIndexFilePath);
|
|
977
|
-
await this.addRoute(existingRoute, newIndexFilePath);
|
|
978
|
-
const newIndexFile = await readFile(newIndexFilePath);
|
|
979
|
-
if (!newIndexFile) {
|
|
980
|
-
throw new Error(`New index file ${newIndexFilePath} not found`);
|
|
981
|
-
}
|
|
982
|
-
this.tsFiles[newIndexFilePath] = newIndexFile;
|
|
983
|
-
await this.handleNonVisualChangeByDeletingIds(newIndexFilePath, newIndexFile);
|
|
984
|
-
this.emit("renamePage", newIndexFilePath);
|
|
985
|
-
// Re-add the watcher
|
|
986
|
-
this.watcher?.add(newPageFolder);
|
|
987
|
-
this.watcher?.add(oldPageFolder);
|
|
988
|
-
};
|
|
989
|
-
getPageRoots(filePath) {
|
|
990
|
-
const scopeFilePath = path.join(path.dirname(filePath), "index.tsx");
|
|
991
|
-
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
992
|
-
if (!currentFile) {
|
|
993
|
-
return null;
|
|
994
|
-
}
|
|
995
|
-
return getPageRoots(filePath, currentFile);
|
|
996
|
-
}
|
|
997
|
-
getScope(filePath) {
|
|
998
|
-
// get the scope.ts file in the same directory as path
|
|
999
|
-
const scopeFilePath = path.join(path.dirname(filePath), SCOPE_FILE);
|
|
1000
|
-
const currentFile = this.sourceTracker?.getCurrentFiles()[scopeFilePath];
|
|
1001
|
-
if (!currentFile) {
|
|
1002
|
-
console.log("File not found", scopeFilePath);
|
|
1003
|
-
return null;
|
|
1004
|
-
}
|
|
1005
|
-
const scope = getScope(scopeFilePath, currentFile);
|
|
1006
|
-
return scope;
|
|
1007
|
-
}
|
|
1008
|
-
getRoutes() {
|
|
1009
|
-
const routes = [];
|
|
1010
|
-
for (const [path, { file }] of Object.entries(this.routes)) {
|
|
1011
|
-
routes.push({
|
|
1012
|
-
path,
|
|
1013
|
-
component: file,
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
return routes;
|
|
1017
|
-
}
|
|
1018
|
-
async addRoute(route, filePath) {
|
|
1019
|
-
if (!this.rootDir) {
|
|
1020
|
-
throw new Error("Root directory not set");
|
|
1021
|
-
}
|
|
1022
|
-
this.routes[route] = { file: this.getRelativeRoutePath(filePath) };
|
|
1023
|
-
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
1024
|
-
}
|
|
1025
|
-
async removeRoute(filePath) {
|
|
1026
|
-
if (!this.rootDir) {
|
|
1027
|
-
throw new Error("Root directory not set");
|
|
1028
|
-
}
|
|
1029
|
-
const relativeFilePath = this.getRelativeRoutePath(filePath);
|
|
1030
|
-
this.routes = Object.fromEntries(Object.entries(this.routes).filter(([_, value]) => value.file !== relativeFilePath));
|
|
1031
|
-
await this.writeFile(path.join(this.rootDir, ROUTES_FILE), JSON.stringify(this.routes, null, 2));
|
|
1032
|
-
}
|
|
1033
|
-
async writeChanges(changes, callback) {
|
|
1034
|
-
return Promise.all(changes.map(async ({ fileName, source, kind }) => {
|
|
1035
|
-
await this.writeFile(fileName, source, kind);
|
|
1036
|
-
return callback?.(fileName, source);
|
|
1037
|
-
}));
|
|
1038
1156
|
}
|
|
1039
|
-
|
|
1040
|
-
return this.
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
const filePath = this.sourceTracker?.getElementToFilePath(id);
|
|
1044
|
-
if (!filePath) {
|
|
1045
|
-
return null;
|
|
1046
|
-
}
|
|
1047
|
-
return this.sourceTracker?.getCurrentFiles()[filePath]?.ast;
|
|
1048
|
-
}
|
|
1049
|
-
renameIdentifierInApis = async ({ elementId, oldName, newName, parentBinding, }) => {
|
|
1050
|
-
const apisInScope = structuredClone(this.getApisInScope(elementId));
|
|
1051
|
-
await this.renameManager.renameEntityInApis({
|
|
1052
|
-
oldName,
|
|
1053
|
-
newName,
|
|
1054
|
-
parentBinding,
|
|
1055
|
-
apis: apisInScope,
|
|
1056
|
-
});
|
|
1057
|
-
// only save the APIs that have changed
|
|
1058
|
-
await Promise.all(apisInScope.map(({ api, filePath }) => {
|
|
1059
|
-
if (isEqual(api?.apiPb, this.apiFiles[filePath]?.apiPb)) {
|
|
1060
|
-
return Promise.resolve();
|
|
1157
|
+
getApiFiles() {
|
|
1158
|
+
return Object.keys(this.apiFiles).reduce((acc, key) => {
|
|
1159
|
+
if (!this.apiFiles[key]) {
|
|
1160
|
+
return acc;
|
|
1061
1161
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return [];
|
|
1069
|
-
}
|
|
1070
|
-
const pagePath = path.dirname(filePath);
|
|
1071
|
-
return Object.entries(this.apiFiles)
|
|
1072
|
-
.filter(([filePath]) => filePath.includes(pagePath))
|
|
1073
|
-
.map(([filePath, api]) => ({ api, filePath }));
|
|
1074
|
-
};
|
|
1075
|
-
getRelativeRoutePath(filePath) {
|
|
1076
|
-
if (!this.rootDir) {
|
|
1077
|
-
throw new Error("Root directory not set");
|
|
1078
|
-
}
|
|
1079
|
-
// no leading slash
|
|
1080
|
-
return path.relative(path.join(this.rootDir, PAGES_DIRECTORY), filePath);
|
|
1162
|
+
acc[key] = {
|
|
1163
|
+
api: this.createClientApi(this.apiFiles[key]),
|
|
1164
|
+
scopeId: this.apiFiles[key].scopeId,
|
|
1165
|
+
};
|
|
1166
|
+
return acc;
|
|
1167
|
+
}, {});
|
|
1081
1168
|
}
|
|
1082
1169
|
// Utilities for converting server API format to Client API format
|
|
1083
1170
|
// We internally save the API as the server does, but we return should always return it
|
|
@@ -1094,19 +1181,21 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
1094
1181
|
},
|
|
1095
1182
|
};
|
|
1096
1183
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1184
|
+
addApiToScope = async (api) => {
|
|
1185
|
+
if (!this.sourceTracker) {
|
|
1186
|
+
throw new Error("Source tracker not initialized");
|
|
1187
|
+
}
|
|
1188
|
+
// We want to add the API entity to our scope, but we do not want to emit entity events, because
|
|
1189
|
+
// the API update event handles this particular side effect.
|
|
1190
|
+
const scopeId = await this.sourceTracker.addApi({
|
|
1191
|
+
pageName: api.pageName,
|
|
1192
|
+
apiName: api.apiPb.metadata.name,
|
|
1193
|
+
});
|
|
1194
|
+
const changes = (await this.sourceTracker?.getAndFlushChanges()) ?? [];
|
|
1195
|
+
await this.writeChanges(changes);
|
|
1196
|
+
return scopeId;
|
|
1197
|
+
};
|
|
1198
|
+
async processApiFileUpdates(filePath, fileType) {
|
|
1110
1199
|
let yamlPath = filePath;
|
|
1111
1200
|
if (fileType.type === "python-api-step" ||
|
|
1112
1201
|
fileType.type === "js-api-step") {
|
|
@@ -1124,7 +1213,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
1124
1213
|
const parsedData = { apiPb: apiContent?.api };
|
|
1125
1214
|
if (!(yamlPath in this.apiFiles &&
|
|
1126
1215
|
isEqual(this.apiFiles[yamlPath]?.apiPb, parsedData.apiPb))) {
|
|
1127
|
-
const { updatedApi, pageName, isNewApi } = this.
|
|
1216
|
+
const { updatedApi, pageName, isNewApi } = this.updateInternalApiData({
|
|
1128
1217
|
api: parsedData.apiPb,
|
|
1129
1218
|
stepPathMap: apiContent.stepPathMap,
|
|
1130
1219
|
}, yamlPath);
|
|
@@ -1135,7 +1224,7 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
1135
1224
|
return;
|
|
1136
1225
|
}
|
|
1137
1226
|
if (isNewApi) {
|
|
1138
|
-
await this.
|
|
1227
|
+
await this.addApiToScope(updatedApi);
|
|
1139
1228
|
}
|
|
1140
1229
|
this.emit("apiManualUpdate", {
|
|
1141
1230
|
api: this.createClientApi(updatedApi),
|
|
@@ -1151,9 +1240,56 @@ export default registerPage(Page, { name: "${name}" });
|
|
|
1151
1240
|
logger.error(`Error updating API: ${yamlPath}, error: ${error}`);
|
|
1152
1241
|
}
|
|
1153
1242
|
}
|
|
1243
|
+
updateInternalApiData = (content, path) => {
|
|
1244
|
+
if (!this.rootDir) {
|
|
1245
|
+
throw new Error("Root directory not set");
|
|
1246
|
+
}
|
|
1247
|
+
const { api: apiContents, stepPathMap } = content;
|
|
1248
|
+
const pageName = getPageName(path);
|
|
1249
|
+
let scopeId = this.sourceTracker?.getScopeDefinitionForPage(pageName)?.id;
|
|
1250
|
+
if (!scopeId) {
|
|
1251
|
+
console.warn("Scope ID not found for API", apiContents.metadata.name);
|
|
1252
|
+
scopeId = "";
|
|
1253
|
+
}
|
|
1254
|
+
const updatedApi = {
|
|
1255
|
+
apiPb: yaml.parse(JSON.stringify(apiContents)),
|
|
1256
|
+
pageName,
|
|
1257
|
+
stepPathMap,
|
|
1258
|
+
scopeId,
|
|
1259
|
+
};
|
|
1260
|
+
const isNewApi = !this.apiFiles[path];
|
|
1261
|
+
this.apiFiles[path] = updatedApi;
|
|
1262
|
+
return { updatedApi, pageName, isNewApi };
|
|
1263
|
+
};
|
|
1264
|
+
renameIdentifierInApis = async ({ elementId, oldName, newName, parentBinding, }) => {
|
|
1265
|
+
const apisInScope = structuredClone(this.getApisInScope(elementId));
|
|
1266
|
+
await this.renameManager.renameEntityInApis({
|
|
1267
|
+
oldName,
|
|
1268
|
+
newName,
|
|
1269
|
+
parentBinding,
|
|
1270
|
+
apis: apisInScope,
|
|
1271
|
+
});
|
|
1272
|
+
// only save the APIs that have changed
|
|
1273
|
+
await Promise.all(apisInScope.map(({ api, filePath }) => {
|
|
1274
|
+
if (isEqual(api?.apiPb, this.apiFiles[filePath]?.apiPb)) {
|
|
1275
|
+
return Promise.resolve();
|
|
1276
|
+
}
|
|
1277
|
+
return this.writeFile(filePath, yaml.stringify(api?.apiPb), "api");
|
|
1278
|
+
}));
|
|
1279
|
+
};
|
|
1280
|
+
getApisInScope = (elementId) => {
|
|
1281
|
+
const filePath = this.sourceTracker?.getElementToFilePath(elementId);
|
|
1282
|
+
if (!filePath) {
|
|
1283
|
+
return [];
|
|
1284
|
+
}
|
|
1285
|
+
const pagePath = path.dirname(filePath);
|
|
1286
|
+
return Object.entries(this.apiFiles)
|
|
1287
|
+
.filter(([filePath]) => filePath.includes(pagePath))
|
|
1288
|
+
.map(([filePath, api]) => ({ api, filePath }));
|
|
1289
|
+
};
|
|
1154
1290
|
}
|
|
1155
1291
|
// Add new mock implementation
|
|
1156
|
-
export class MockFileSyncManager extends
|
|
1292
|
+
export class MockFileSyncManager extends FileSystemManager {
|
|
1157
1293
|
tsFiles = {};
|
|
1158
1294
|
apiFiles = {};
|
|
1159
1295
|
async watch(_watcher, _path) {
|
|
@@ -1207,8 +1343,7 @@ async function readFile(path) {
|
|
|
1207
1343
|
// in order for try-catch to work, we need to intentionally await the readFile call,
|
|
1208
1344
|
// otherwise the error won't be be caught
|
|
1209
1345
|
// see: https://github.com/nodejs/node/issues/51894#issuecomment-1974017737
|
|
1210
|
-
|
|
1211
|
-
return content;
|
|
1346
|
+
return await fs.readFile(path, "utf-8");
|
|
1212
1347
|
}
|
|
1213
1348
|
catch (e) {
|
|
1214
1349
|
getLogger().error(`error reading file: ${path}`, getErrorMeta(e));
|
|
@@ -1229,12 +1364,4 @@ async function readFiles(dir) {
|
|
|
1229
1364
|
const getMergedApiContent = async (path) => {
|
|
1230
1365
|
return readAppApiYamlFile(path.split("/").slice(0, -1).join("/"));
|
|
1231
1366
|
};
|
|
1232
|
-
const getPageName = (path) => {
|
|
1233
|
-
const parts = path.split("/");
|
|
1234
|
-
const pagesIndex = parts.findIndex((part) => part === "pages");
|
|
1235
|
-
if (pagesIndex !== -1 && parts[pagesIndex + 1]) {
|
|
1236
|
-
return parts[pagesIndex + 1] ?? "";
|
|
1237
|
-
}
|
|
1238
|
-
return "";
|
|
1239
|
-
};
|
|
1240
1367
|
//# sourceMappingURL=file-system-manager.js.map
|