sfdx-git-delta 7.4.0 → 7.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -3
- package/lib/adapter/GitAdapter.d.ts +5 -3
- package/lib/adapter/GitAdapter.js +71 -32
- package/lib/adapter/GitAdapter.js.map +1 -1
- package/lib/adapter/pathMatching.d.ts +2 -1
- package/lib/adapter/pathMatching.js +35 -3
- package/lib/adapter/pathMatching.js.map +1 -1
- package/lib/adapter/treeReader.d.ts +1 -0
- package/lib/adapter/treeReader.js +10 -0
- package/lib/adapter/treeReader.js.map +1 -1
- package/lib/adapter/tsgitErrorMap.d.ts +5 -4
- package/lib/adapter/tsgitErrorMap.js +12 -5
- package/lib/adapter/tsgitErrorMap.js.map +1 -1
- package/lib/constant/metadataConstants.d.ts +12 -1
- package/lib/constant/metadataConstants.js +28 -3
- package/lib/constant/metadataConstants.js.map +1 -1
- package/lib/main.js +66 -63
- package/lib/main.js.map +1 -1
- package/lib/metadata/MetadataRepositoryImpl.d.ts +14 -2
- package/lib/metadata/MetadataRepositoryImpl.js +192 -50
- package/lib/metadata/MetadataRepositoryImpl.js.map +1 -1
- package/lib/post-processor/changesManifestProcessor.js +14 -1
- package/lib/post-processor/changesManifestProcessor.js.map +1 -1
- package/lib/post-processor/includeProcessor.js +8 -9
- package/lib/post-processor/includeProcessor.js.map +1 -1
- package/lib/service/botHandler.js +1 -1
- package/lib/service/botHandler.js.map +1 -1
- package/lib/service/inBundleHandler.js +10 -23
- package/lib/service/inBundleHandler.js.map +1 -1
- package/lib/service/inFolderHandler.js +1 -2
- package/lib/service/inFolderHandler.js.map +1 -1
- package/lib/service/objectTranslationHandler.d.ts +0 -1
- package/lib/service/objectTranslationHandler.js +11 -4
- package/lib/service/objectTranslationHandler.js.map +1 -1
- package/lib/types/runContext.d.ts +16 -5
- package/lib/types/runContext.js +17 -5
- package/lib/types/runContext.js.map +1 -1
- package/lib/utils/__mocks__/LoggingService.d.ts +2 -7
- package/lib/utils/__mocks__/LoggingService.js.map +1 -1
- package/lib/utils/changeSet.d.ts +4 -0
- package/lib/utils/changeSet.js +87 -37
- package/lib/utils/changeSet.js.map +1 -1
- package/lib/utils/configValidator.d.ts +11 -1
- package/lib/utils/configValidator.js +133 -80
- package/lib/utils/configValidator.js.map +1 -1
- package/lib/utils/errorUtils.d.ts +5 -0
- package/lib/utils/errorUtils.js +8 -0
- package/lib/utils/errorUtils.js.map +1 -1
- package/lib/utils/messageSanitizer.d.ts +1 -0
- package/lib/utils/messageSanitizer.js +46 -0
- package/lib/utils/messageSanitizer.js.map +1 -1
- package/lib/utils/metadataBoundaryResolver.d.ts +4 -0
- package/lib/utils/metadataBoundaryResolver.js +40 -29
- package/lib/utils/metadataBoundaryResolver.js.map +1 -1
- package/lib/utils/metadataDiff/deepEqualJson.js +51 -44
- package/lib/utils/metadataDiff/deepEqualJson.js.map +1 -1
- package/lib/utils/metadataDiff/xmlEventReader.js +60 -50
- package/lib/utils/metadataDiff/xmlEventReader.js.map +1 -1
- package/lib/utils/repoGitDiff.d.ts +20 -2
- package/lib/utils/repoGitDiff.js +129 -35
- package/lib/utils/repoGitDiff.js.map +1 -1
- package/lib/utils/treeIndexScope.js +25 -37
- package/lib/utils/treeIndexScope.js.map +1 -1
- package/lib/utils/treeReaderBuilder.d.ts +8 -0
- package/lib/utils/treeReaderBuilder.js +34 -0
- package/lib/utils/treeReaderBuilder.js.map +1 -0
- package/lib/utils/txmlAdapter.js +1 -1
- package/lib/utils/txmlAdapter.js.map +1 -1
- package/messages/delta.md +16 -5
- package/oclif.manifest.json +1 -1
- package/package.json +90 -49
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
export const BOT_TYPE = 'Bot';
|
|
3
|
+
export const BOT_VERSION_TYPE = 'BotVersion';
|
|
4
|
+
export const CUSTOM_APPLICATION_SUFFIX = 'app';
|
|
5
|
+
export const CUSTOM_METADATA_SUFFIX = 'md';
|
|
6
|
+
export const DASHBOARD_TYPE = 'Dashboard';
|
|
7
|
+
// A component named by the single segment below its type directory: every
|
|
8
|
+
// content-container adapter except digitalExperience (depths below), and the
|
|
9
|
+
// holder-scoped composed types, whose every file keys on that one segment.
|
|
10
|
+
export const DEFAULT_CONTAINER_DEPTH = 1;
|
|
11
|
+
export const DIGITAL_EXPERIENCE_ADAPTER = 'digitalExperience';
|
|
2
12
|
// SDR adapters whose component is a directory owning an opaque content subtree
|
|
3
13
|
// (StaticResource/ExperienceBundle/Document via mixedContent, Aura/LWC via
|
|
4
14
|
// bundle, DigitalExperienceBundle via digitalExperience). When such a type is
|
|
@@ -6,18 +16,22 @@
|
|
|
6
16
|
// metadata directories — so type resolution must stop there.
|
|
7
17
|
export const CONTENT_CONTAINER_ADAPTERS = new Set([
|
|
8
18
|
'bundle',
|
|
9
|
-
|
|
19
|
+
DIGITAL_EXPERIENCE_ADAPTER,
|
|
10
20
|
'mixedContent',
|
|
11
21
|
]);
|
|
12
|
-
export const CUSTOM_APPLICATION_SUFFIX = 'app';
|
|
13
|
-
export const CUSTOM_METADATA_SUFFIX = 'md';
|
|
14
22
|
export const DIGITAL_EXPERIENCE_BUNDLE_TYPE = 'DigitalExperienceBundle';
|
|
23
|
+
// SDR's canonical layout below `digitalExperiences/` is
|
|
24
|
+
// `<baseType>/<spaceApiName>/<contentType>/<contentApiName>/<file>`: the
|
|
25
|
+
// bundle is the first two segments, a page content folder the first four.
|
|
26
|
+
export const DIGITAL_EXPERIENCE_BUNDLE_DEPTH = 2;
|
|
27
|
+
export const DIGITAL_EXPERIENCE_CONTENT_DEPTH = 4;
|
|
15
28
|
export const DIGITAL_EXPERIENCE_TYPE = 'DigitalExperience';
|
|
16
29
|
export const EMAIL_SERVICES_FUNCTION_SUFFIX = 'xml';
|
|
17
30
|
export const FIELD_DIRECTORY_NAME = 'fields';
|
|
18
31
|
export const FLOW_DEFINITIONS_KEY = 'flowDefinitions';
|
|
19
32
|
export const FLOW_XML_NAME = 'Flow';
|
|
20
33
|
export const INFOLDER_SUFFIX = `Folder`;
|
|
34
|
+
export const INFOLDER_SUFFIX_REGEX = new RegExp(`${INFOLDER_SUFFIX}$`);
|
|
21
35
|
export const LABEL_DECOMPOSED_SUFFIX = 'label';
|
|
22
36
|
export const MASTER_DETAIL_TAG = '<type>MasterDetail</type>';
|
|
23
37
|
export const METAFILE_SUFFIX = '-meta.xml';
|
|
@@ -26,6 +40,16 @@ export const OBJECT_TRANSLATION_META_XML_SUFFIX = `objectTranslation${METAFILE_S
|
|
|
26
40
|
export const OBJECT_TRANSLATION_TYPE = 'CustomObjectTranslation';
|
|
27
41
|
export const OBJECT_TYPE = 'CustomObject';
|
|
28
42
|
export const PERMISSIONSET_TYPE = 'PermissionSet';
|
|
43
|
+
export const REPORT_TYPE = 'Report';
|
|
44
|
+
// The Metadata API relocates these when the package lists them under a new
|
|
45
|
+
// folder, so their former path must not be destroyed alongside the move.
|
|
46
|
+
// Declared out of alphabetical order on purpose: the Set dereferences both
|
|
47
|
+
// members at module evaluation, so filing it under F would read REPORT_TYPE
|
|
48
|
+
// before its initialiser has run.
|
|
49
|
+
export const FOLDER_MOVE_ON_DEPLOY_TYPES = new Set([
|
|
50
|
+
REPORT_TYPE,
|
|
51
|
+
DASHBOARD_TYPE,
|
|
52
|
+
]);
|
|
29
53
|
export const SHARING_RULE_TYPE = 'SharingRules';
|
|
30
54
|
export const SUB_OBJECT_TYPES = [
|
|
31
55
|
'BusinessProcess',
|
|
@@ -54,5 +78,6 @@ export const SUB_OBJECT_TYPES = [
|
|
|
54
78
|
];
|
|
55
79
|
export const TRANSLATION_EXTENSION = 'translation';
|
|
56
80
|
export const TRANSLATION_TYPE = 'Translations';
|
|
81
|
+
export const VIRTUAL_BOT_TYPE = 'VirtualBot';
|
|
57
82
|
export const WORKFLOW_TYPE = 'Workflow';
|
|
58
83
|
//# sourceMappingURL=metadataConstants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadataConstants.js","sourceRoot":"","sources":["../../src/constant/metadataConstants.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,+EAA+E;AAC/E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IAChD,QAAQ;IACR,
|
|
1
|
+
{"version":3,"file":"metadataConstants.js","sourceRoot":"","sources":["../../src/constant/metadataConstants.ts"],"names":[],"mappings":"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"}
|
package/lib/main.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
import GitAdapter from './adapter/GitAdapter.js';
|
|
3
3
|
import IOExecutor from './adapter/ioExecutor.js';
|
|
4
|
-
import { createTreeReader, EMPTY_TREE_READER } from './adapter/treeReader.js';
|
|
5
4
|
import { getDefinition } from './metadata/metadataManager.js';
|
|
6
5
|
import { getPostProcessors } from './post-processor/postProcessorManager.js';
|
|
7
6
|
import DiffLineInterpreter from './service/diffLineInterpreter.js';
|
|
@@ -15,6 +14,14 @@ import { parseSourceDirs } from './utils/pathspec.js';
|
|
|
15
14
|
import RenameResolver from './utils/renameResolver.js';
|
|
16
15
|
import RepoGitDiff from './utils/repoGitDiff.js';
|
|
17
16
|
import { computeTreeIndexScope } from './utils/treeIndexScope.js';
|
|
17
|
+
import { buildRunTreeReader } from './utils/treeReaderBuilder.js';
|
|
18
|
+
const collectLines = async (lines) => {
|
|
19
|
+
const materialized = [];
|
|
20
|
+
for await (const line of lines) {
|
|
21
|
+
materialized.push(line);
|
|
22
|
+
}
|
|
23
|
+
return materialized;
|
|
24
|
+
};
|
|
18
25
|
export default async (configInput) => {
|
|
19
26
|
// Stryker disable next-line StringLiteral -- equivalent: log content is observability only; tests assert on the returned Work, not lazy log lines
|
|
20
27
|
Logger.trace('main: entry');
|
|
@@ -30,55 +37,28 @@ export default async (configInput) => {
|
|
|
30
37
|
const configWarnings = await new ConfigValidator(config, rejections).validateConfig();
|
|
31
38
|
const metadata = await getDefinition(config);
|
|
32
39
|
const repoGitDiffHelper = new RepoGitDiff(config, metadata);
|
|
33
|
-
// The
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// still emitting
|
|
38
|
-
const needsScopeFromDiff =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// built under — the lesson from the shared, scope-keyed cache this
|
|
56
|
-
// replaces (see design history) is that a cache keyed by a value each
|
|
57
|
-
// reader recomputes is an implicit contract that will drift.
|
|
58
|
-
let trees = EMPTY_TREE_READER;
|
|
59
|
-
if (config.generateDelta) {
|
|
60
|
-
const gitAdapter = GitAdapter.getInstance(config);
|
|
61
|
-
let scopePaths = config.source;
|
|
62
|
-
if (needsScopeFromDiff) {
|
|
63
|
-
scopePaths = [
|
|
64
|
-
...computeTreeIndexScope(lines, metadata),
|
|
65
|
-
];
|
|
66
|
-
}
|
|
67
|
-
if (scopePaths.length > 0) {
|
|
68
|
-
const [toIndex, fromIndex] = await Promise.all([
|
|
69
|
-
gitAdapter.buildTreeIndex(config.to, scopePaths),
|
|
70
|
-
gitAdapter.buildTreeIndex(config.from, scopePaths),
|
|
71
|
-
]);
|
|
72
|
-
const entries = new Map();
|
|
73
|
-
// Stryker disable ConditionalExpression -- equivalent: forcing either guard to always run stores `undefined` at that revision key instead of skipping it, but the only reader is createTreeReader's `entries.get(revision) ?? NO_INDEX`, and Map.get answers `undefined` for an absent key and an explicitly-undefined value alike. `entries` is local to this block and exposes no has/size/enumeration, so no observer can tell the two apart
|
|
74
|
-
if (toIndex)
|
|
75
|
-
entries.set(config.to, toIndex);
|
|
76
|
-
if (fromIndex)
|
|
77
|
-
entries.set(config.from, fromIndex);
|
|
78
|
-
// Stryker restore ConditionalExpression
|
|
79
|
-
trees = createTreeReader(entries);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
40
|
+
// The tree-index scope is read off the diff whenever no include file
|
|
41
|
+
// overrides it, so the stream is materialised once: computeTreeIndexScope
|
|
42
|
+
// and lineProcessor.process walk the same lines. With an include file the
|
|
43
|
+
// scope is config.source and the async iterable feeds straight into
|
|
44
|
+
// DiffLineInterpreter, so handlers fire while git is still emitting.
|
|
45
|
+
const needsScopeFromDiff = !config.include && !config.includeDestructive;
|
|
46
|
+
const materialized = needsScopeFromDiff
|
|
47
|
+
? await collectLines(repoGitDiffHelper.getLines())
|
|
48
|
+
: undefined;
|
|
49
|
+
const lines = materialized ?? repoGitDiffHelper.getLines();
|
|
50
|
+
// The manifests must not depend on --generate-delta: every index-backed
|
|
51
|
+
// read that decides package.xml vs destructiveChanges.xml (container
|
|
52
|
+
// liveness, decomposed-holder liveness, the include listing) runs in
|
|
53
|
+
// every mode, so the run's two indexes are built in every mode. The flag
|
|
54
|
+
// gates only what is copied. Built here and threaded to every reader
|
|
55
|
+
// (DiffLineInterpreter, RenameResolver, IOExecutor, the post-processors)
|
|
56
|
+
// through RunContext rather than cached on GitAdapter, so no reader can
|
|
57
|
+
// see a different scope than the one this run built under.
|
|
58
|
+
const scopePaths = materialized
|
|
59
|
+
? [...computeTreeIndexScope(materialized, metadata)]
|
|
60
|
+
: config.source;
|
|
61
|
+
const { trees, unindexed } = await buildRunTreeReader(GitAdapter.getInstance(config), config, scopePaths);
|
|
82
62
|
const ctx = { config, metadata, trees };
|
|
83
63
|
const lineProcessor = new DiffLineInterpreter(ctx);
|
|
84
64
|
const postProcessors = getPostProcessors(ctx);
|
|
@@ -102,20 +82,41 @@ export default async (configInput) => {
|
|
|
102
82
|
// changes at all — a non-empty manifest (e.g. members sourced by
|
|
103
83
|
// --include-file independent of the diff) means the scope did its
|
|
104
84
|
// job, so naming it as unmatched would be misleading.
|
|
85
|
+
const messages = new MessageService();
|
|
86
|
+
// tokens is a thunk, not a precomputed array: sanitizeForMessage must
|
|
87
|
+
// only run when the warning actually fires, matching the ternaries this
|
|
88
|
+
// replaces (some callers never set config.to/from, so an unconditional
|
|
89
|
+
// sanitizeForMessage(requestedTo) would crash on undefined).
|
|
90
|
+
const warnIf = (condition, key, tokens) => condition ? [new Error(messages.getMessage(key, tokens()))] : [];
|
|
105
91
|
const unmatchedScopes = repoGitDiffHelper.getUnmatchedSourceScopes();
|
|
106
|
-
const sourceScopeWarnings = unmatchedScopes.length > 0 && changes.isEmpty()
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
92
|
+
const sourceScopeWarnings = warnIf(unmatchedScopes.length > 0 && changes.isEmpty(), 'warning.SourceDirMatchedNothing', () => [
|
|
93
|
+
// Sanitize each scope before joining: the length cap in
|
|
94
|
+
// sanitizeForMessage must apply per scope, not to the
|
|
95
|
+
// joined aggregate, or one long scope name silently
|
|
96
|
+
// elides every scope listed after it.
|
|
97
|
+
unmatchedScopes.map(sanitizeForMessage).join(', '),
|
|
98
|
+
sanitizeForMessage(requestedFrom),
|
|
99
|
+
sanitizeForMessage(requestedTo),
|
|
100
|
+
]);
|
|
101
|
+
// Also final by this point. When the `to` listing could not be read,
|
|
102
|
+
// components moved into an ignored directory were not recognised as
|
|
103
|
+
// moves, so their deletions are reported destructively. That is the
|
|
104
|
+
// safe direction, but it is a degraded answer and must not be silent.
|
|
105
|
+
const probeFailure = repoGitDiffHelper.getHeldAdditionProbeFailure();
|
|
106
|
+
const ignoredMoveWarnings = warnIf(!!probeFailure, 'warning.IgnoredMoveCheckSkipped',
|
|
107
|
+
// Non-null: this thunk only runs when warnIf's condition (!!probeFailure)
|
|
108
|
+
// is true, but the closure sits outside TS's narrowing of that check.
|
|
109
|
+
() => [
|
|
110
|
+
sanitizeForMessage(requestedTo),
|
|
111
|
+
probeFailure.candidateCount.toString(),
|
|
112
|
+
]);
|
|
113
|
+
// Known as soon as the tree reader is built, but raised here with the
|
|
114
|
+
// rest of the warnings for a single review point. Only `to` is
|
|
115
|
+
// warn-worthy: it is the revision every liveness read (pathExists,
|
|
116
|
+
// readDirs) resolves through, so its degrade is what lets a live
|
|
117
|
+
// container land in destructiveChanges.xml. `from` only feeds deep-path
|
|
118
|
+
// member-name resolution, a narrower and already-attributed effect.
|
|
119
|
+
const treeIndexWarnings = warnIf(unindexed.includes(config.to), 'warning.TreeIndexUnavailable', () => [sanitizeForMessage(requestedTo)]);
|
|
119
120
|
const work = {
|
|
120
121
|
config,
|
|
121
122
|
changes,
|
|
@@ -124,6 +125,8 @@ export default async (configInput) => {
|
|
|
124
125
|
...assemblyWarnings,
|
|
125
126
|
...processorWarnings,
|
|
126
127
|
...sourceScopeWarnings,
|
|
128
|
+
...ignoredMoveWarnings,
|
|
129
|
+
...treeIndexWarnings,
|
|
127
130
|
],
|
|
128
131
|
};
|
|
129
132
|
// Stryker disable next-line StringLiteral -- equivalent: log content is observability only
|
package/lib/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,OAAO,UAAU,MAAM,yBAAyB,CAAA;AAChD,OAAO,UAAU,MAAM,yBAAyB,CAAA;AAEhD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,21 +6,33 @@ export declare class MetadataRepositoryImpl implements MetadataRepository {
|
|
|
6
6
|
protected readonly metadataPerDir: Map<string, Metadata>;
|
|
7
7
|
protected readonly metadataPerXmlName: Map<string, Metadata>;
|
|
8
8
|
private readonly pathCache;
|
|
9
|
+
private readonly unsafeExtensions;
|
|
9
10
|
constructor(metadatas: Metadata[]);
|
|
10
11
|
protected addSuffix(metadata: Metadata): void;
|
|
12
|
+
private registerSuffix;
|
|
11
13
|
protected addSharedFolderSuffix(metadata: Metadata): void;
|
|
12
14
|
protected addFolder(metadata: Metadata): void;
|
|
13
15
|
protected addXmlName(metadata: Metadata): void;
|
|
14
16
|
has(path: string): boolean;
|
|
15
17
|
get(path: string): Metadata | undefined;
|
|
18
|
+
private resolve;
|
|
16
19
|
protected searchByExtension(parts: string[]): Metadata | undefined;
|
|
17
20
|
protected searchByDirectory(parts: string[]): Metadata | undefined;
|
|
18
21
|
protected ownsNestedPaths(metadata: Metadata): boolean;
|
|
22
|
+
private isContentContainer;
|
|
19
23
|
private declaresNestedContent;
|
|
20
|
-
protected searchByXmlName(xmlName: string): Metadata | undefined;
|
|
21
24
|
getByXmlName(xmlName: string): Metadata | undefined;
|
|
22
25
|
getFullyQualifiedName(path: string): string;
|
|
26
|
+
private plainTypeName;
|
|
27
|
+
private composedTypeName;
|
|
28
|
+
private holderScopedName;
|
|
29
|
+
private componentScopedName;
|
|
30
|
+
private folderScopedName;
|
|
31
|
+
private nestedContentName;
|
|
32
|
+
private containerName;
|
|
33
|
+
private containerDepth;
|
|
23
34
|
values(): Metadata[];
|
|
24
|
-
private static UNSAFE_EXTENSION;
|
|
25
35
|
private static COMPOSED_TYPES;
|
|
36
|
+
private static HOLDER_SCOPED_COMPOSED_TYPES;
|
|
37
|
+
private static FOLDER_SCOPED_TYPES;
|
|
26
38
|
}
|
|
@@ -6,23 +6,41 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
8
|
import { parse } from 'node:path/posix';
|
|
9
|
-
import { DOT, PATH_SEP } from '../constant/fsConstants.js';
|
|
10
|
-
import {
|
|
9
|
+
import { DOT, EXTENSION_SUFFIX_REGEX, PATH_SEP, } from '../constant/fsConstants.js';
|
|
10
|
+
import { GIT_DIFF_TYPE_REGEX } from '../constant/gitConstants.js';
|
|
11
|
+
import { CONTENT_CONTAINER_ADAPTERS, CUSTOM_APPLICATION_SUFFIX, CUSTOM_METADATA_SUFFIX, DEFAULT_CONTAINER_DEPTH, DIGITAL_EXPERIENCE_ADAPTER, DIGITAL_EXPERIENCE_BUNDLE_DEPTH, DIGITAL_EXPERIENCE_CONTENT_DEPTH, EMAIL_SERVICES_FUNCTION_SUFFIX, INFOLDER_SUFFIX_REGEX, META_REGEX, METAFILE_SUFFIX, OBJECT_TRANSLATION_TYPE, OBJECT_TYPE, PERMISSIONSET_TYPE, SHARING_RULE_TYPE, SUB_OBJECT_TYPES, VIRTUAL_BOT_TYPE, WORKFLOW_TYPE, } from '../constant/metadataConstants.js';
|
|
11
12
|
import { log } from '../utils/LoggingDecorator.js';
|
|
12
|
-
|
|
13
|
+
// Callers hand over a git diff line as readily as a bare path, and the
|
|
14
|
+
// status prefix rides on the first segment — which is exactly where a
|
|
15
|
+
// repository laid out at its own root keeps the type directory — so nothing
|
|
16
|
+
// may compare segments before this has run.
|
|
17
|
+
const asFilePath = (path) => path.replace(GIT_DIFF_TYPE_REGEX, '');
|
|
18
|
+
// Shared by every scoped-name arm that owns a metaFile companion, so the
|
|
19
|
+
// `-meta.xml` strip is a single call site both `containerName` and
|
|
20
|
+
// `folderScopedName` route through.
|
|
21
|
+
const withoutMetaSuffix = (name) => name.replace(META_REGEX, '');
|
|
13
22
|
export class MetadataRepositoryImpl {
|
|
14
23
|
metadatas;
|
|
15
24
|
metadataPerExt;
|
|
16
25
|
metadataPerDir;
|
|
17
26
|
metadataPerXmlName;
|
|
18
27
|
// Memoizes get(path) results across the registry's lifetime. The lookup
|
|
19
|
-
// chain (split + extension + directory walk
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
28
|
+
// chain (split + extension + directory walk) is deterministic in `path`
|
|
29
|
+
// and the registry is read-only after construction, so a single cache
|
|
30
|
+
// here is safe and frees every consumer (has, getFullyQualifiedName,
|
|
31
|
+
// TypeHandlerFactory, computeTreeIndexScope, the RepoGitDiff filter
|
|
32
|
+
// chain) from repeating the work. Stores `undefined` negatives too —
|
|
33
|
+
// distinguished from "uncached" via .has().
|
|
25
34
|
pathCache = new Map();
|
|
35
|
+
// A suffix declared by more than one type is ambiguous for this registry
|
|
36
|
+
// only. Kept per instance rather than shared across the process, so a
|
|
37
|
+
// registry built from an additional user-supplied file cannot make an
|
|
38
|
+
// extension unsafe for every registry built after it.
|
|
39
|
+
unsafeExtensions = new Set([
|
|
40
|
+
CUSTOM_APPLICATION_SUFFIX,
|
|
41
|
+
EMAIL_SERVICES_FUNCTION_SUFFIX,
|
|
42
|
+
CUSTOM_METADATA_SUFFIX,
|
|
43
|
+
]);
|
|
26
44
|
constructor(metadatas) {
|
|
27
45
|
this.metadatas = metadatas;
|
|
28
46
|
this.metadataPerExt = new Map();
|
|
@@ -35,24 +53,36 @@ export class MetadataRepositoryImpl {
|
|
|
35
53
|
});
|
|
36
54
|
}
|
|
37
55
|
addSuffix(metadata) {
|
|
38
|
-
|
|
39
|
-
if (metadata.suffix) {
|
|
40
|
-
if (this.metadataPerExt.has(metadata.suffix)) {
|
|
41
|
-
MetadataRepositoryImpl.UNSAFE_EXTENSION.add(metadata.suffix);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
this.metadataPerExt.set(metadata.suffix, metadata);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
56
|
+
this.registerSuffix(metadata);
|
|
47
57
|
this.addSharedFolderSuffix(metadata);
|
|
48
58
|
}
|
|
59
|
+
// Content clones must register through here, not through addSuffix: a clone
|
|
60
|
+
// keeps the same content[] as its source (see addSharedFolderSuffix below),
|
|
61
|
+
// so routing it back through addSuffix would re-enter addSharedFolderSuffix
|
|
62
|
+
// on that same content[] and recurse forever.
|
|
63
|
+
registerSuffix(metadata) {
|
|
64
|
+
// Stryker disable next-line ConditionalExpression -- equivalent: suffix presence guard; flipping to false lets a suffix-less entry reach the Map.has lookup on undefined, which returns false and falls into metadataPerExt.set under an undefined key — a stray entry no test path queries by undefined suffix (the true flip registers nothing and is killed by the suite)
|
|
65
|
+
if (!metadata.suffix)
|
|
66
|
+
return;
|
|
67
|
+
if (this.metadataPerExt.has(metadata.suffix)) {
|
|
68
|
+
this.unsafeExtensions.add(metadata.suffix);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.metadataPerExt.set(metadata.suffix, metadata);
|
|
72
|
+
}
|
|
73
|
+
// content is kept on the clone (not stripped) because it is what tells the
|
|
74
|
+
// rest of the registry — ownsNestedPaths, via declaresNestedContent — that
|
|
75
|
+
// this suffix's type owns every path nested below the shared directory,
|
|
76
|
+
// not just the file itself. Dropping it here would make a component
|
|
77
|
+
// resolved through its content suffix indistinguishable from a type with no
|
|
78
|
+
// nested content, losing the folder segment that tells same-named
|
|
79
|
+
// components under different folders apart.
|
|
49
80
|
addSharedFolderSuffix(metadata) {
|
|
50
81
|
if (!metadata.content)
|
|
51
82
|
return;
|
|
52
|
-
const { content: _content, ...metadataWithoutContent } = metadata;
|
|
53
83
|
for (const sharedFolderMetadataDef of metadata.content) {
|
|
54
|
-
this.
|
|
55
|
-
...
|
|
84
|
+
this.registerSuffix({
|
|
85
|
+
...metadata,
|
|
56
86
|
suffix: sharedFolderMetadataDef.suffix,
|
|
57
87
|
});
|
|
58
88
|
}
|
|
@@ -73,19 +103,23 @@ export class MetadataRepositoryImpl {
|
|
|
73
103
|
return !!this.get(path);
|
|
74
104
|
}
|
|
75
105
|
get(path) {
|
|
106
|
+
return this.resolve(asFilePath(path));
|
|
107
|
+
}
|
|
108
|
+
// Split out of get() so the key rule, which already holds the stripped
|
|
109
|
+
// path, resolves it without stripping twice; every other caller goes
|
|
110
|
+
// through get() and its single strip.
|
|
111
|
+
resolve(filePath) {
|
|
76
112
|
// Stryker disable next-line ConditionalExpression -- equivalent: cache short-circuit; flipping to false re-runs the search chain which is deterministic in path, so the result is identical
|
|
77
|
-
if (this.pathCache.has(
|
|
78
|
-
return this.pathCache.get(
|
|
79
|
-
const parts =
|
|
80
|
-
const result = this.searchByExtension(parts) ??
|
|
81
|
-
|
|
82
|
-
this.searchByXmlName(path);
|
|
83
|
-
this.pathCache.set(path, result);
|
|
113
|
+
if (this.pathCache.has(filePath))
|
|
114
|
+
return this.pathCache.get(filePath);
|
|
115
|
+
const parts = filePath.split(PATH_SEP);
|
|
116
|
+
const result = this.searchByExtension(parts) ?? this.searchByDirectory(parts);
|
|
117
|
+
this.pathCache.set(filePath, result);
|
|
84
118
|
return result;
|
|
85
119
|
}
|
|
86
120
|
searchByExtension(parts) {
|
|
87
121
|
const extension = parse(parts[parts.length - 1].replace(METAFILE_SUFFIX, '')).ext.replace(DOT, '');
|
|
88
|
-
if (
|
|
122
|
+
if (this.unsafeExtensions.has(extension)) {
|
|
89
123
|
return;
|
|
90
124
|
}
|
|
91
125
|
return this.metadataPerExt.get(extension);
|
|
@@ -113,11 +147,16 @@ export class MetadataRepositoryImpl {
|
|
|
113
147
|
// directories.
|
|
114
148
|
ownsNestedPaths(metadata) {
|
|
115
149
|
return (metadata.inFolder ||
|
|
116
|
-
|
|
117
|
-
(metadata.adapter !== undefined &&
|
|
118
|
-
CONTENT_CONTAINER_ADAPTERS.has(metadata.adapter)) ||
|
|
150
|
+
this.isContentContainer(metadata) ||
|
|
119
151
|
this.declaresNestedContent(metadata));
|
|
120
152
|
}
|
|
153
|
+
// Extracted so componentScopedName can pick a container's nesting depth
|
|
154
|
+
// from its adapter without re-deriving what counts as a container.
|
|
155
|
+
isContentContainer(metadata) {
|
|
156
|
+
const adapter = metadata.adapter;
|
|
157
|
+
// Stryker disable next-line ConditionalExpression -- equivalent: Set.has(undefined) is already false, so the `!== undefined` operand changes no runtime outcome; it exists only to narrow string|undefined → string for the Set<string>.has call under strict mode
|
|
158
|
+
return adapter !== undefined && CONTENT_CONTAINER_ADAPTERS.has(adapter);
|
|
159
|
+
}
|
|
121
160
|
// A type declares nested content when its `content[]` lists suffix-keyed
|
|
122
161
|
// sub-types sharing one directory (Wave/Bot/Discovery/Moderation): the
|
|
123
162
|
// segments below that directory are then user-named content, not metadata
|
|
@@ -134,33 +173,122 @@ export class MetadataRepositoryImpl {
|
|
|
134
173
|
declaresNestedContent(metadata) {
|
|
135
174
|
return (metadata.content?.length ?? 0) > 0;
|
|
136
175
|
}
|
|
137
|
-
searchByXmlName(xmlName) {
|
|
138
|
-
return this.metadataPerXmlName.get(xmlName);
|
|
139
|
-
}
|
|
140
176
|
getByXmlName(xmlName) {
|
|
141
177
|
return this.metadataPerXmlName.get(xmlName);
|
|
142
178
|
}
|
|
143
179
|
getFullyQualifiedName(path) {
|
|
144
|
-
|
|
145
|
-
const type = this.
|
|
146
|
-
if (type
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
180
|
+
const filePath = asFilePath(path);
|
|
181
|
+
const type = this.resolve(filePath);
|
|
182
|
+
if (!type)
|
|
183
|
+
return parse(filePath).base;
|
|
184
|
+
if (MetadataRepositoryImpl.HOLDER_SCOPED_COMPOSED_TYPES.has(type.xmlName))
|
|
185
|
+
return this.holderScopedName(filePath, type);
|
|
186
|
+
if (MetadataRepositoryImpl.COMPOSED_TYPES.has(type.xmlName))
|
|
187
|
+
return this.composedTypeName(filePath);
|
|
188
|
+
if (!this.ownsNestedPaths(type))
|
|
189
|
+
return this.plainTypeName(filePath, type);
|
|
190
|
+
return this.componentScopedName(filePath, type);
|
|
191
|
+
}
|
|
192
|
+
// The type directory leads a plain key because a suffix belongs to no
|
|
193
|
+
// single type (`policy`, `settings`, `site`, `rule` are each declared by
|
|
194
|
+
// several) — it is the registry's directory, not the file's own, which is
|
|
195
|
+
// what lets a component moved between package directories key the same.
|
|
196
|
+
// Four live registry entries (AssignmentRule, AutoResponseRule,
|
|
197
|
+
// EscalationRule, ManagedTopic) carry a suffix and no directory: the key
|
|
198
|
+
// must never read `undefined/…`.
|
|
199
|
+
plainTypeName(path, type) {
|
|
200
|
+
const fileName = parse(path).base.replace(META_REGEX, '');
|
|
201
|
+
return type.directoryName ? `${type.directoryName}/${fileName}` : fileName;
|
|
202
|
+
}
|
|
203
|
+
// Anchored segment-wise on the first registry directory in the path: a
|
|
204
|
+
// substring search would also hit an unrelated directory that merely
|
|
205
|
+
// contains that name (`objects_backup/objects/…`) and drag it into the key.
|
|
206
|
+
// With no such segment findIndex answers -1 and slice(-1) keeps only the
|
|
207
|
+
// file name, so the fallback every other arm spells out needs no branch.
|
|
208
|
+
composedTypeName(path) {
|
|
209
|
+
const parts = path.split(PATH_SEP);
|
|
210
|
+
const typeIndex = parts.findIndex(part => this.metadataPerDir.has(part));
|
|
211
|
+
return parts.slice(typeIndex).join('');
|
|
212
|
+
}
|
|
213
|
+
// Depth 1 handles every decomposed spelling a holder can appear under: a
|
|
214
|
+
// single file directly beneath its type directory, or a child file nested
|
|
215
|
+
// several segments down — both collapse to the same one-segment container
|
|
216
|
+
// name, because the holder is one component regardless of how SFDX
|
|
217
|
+
// decomposed it into files.
|
|
218
|
+
holderScopedName(path, type) {
|
|
219
|
+
const parts = path.split(PATH_SEP);
|
|
220
|
+
const typeIndex = parts.lastIndexOf(type.directoryName);
|
|
221
|
+
if (typeIndex === -1)
|
|
222
|
+
return parse(path).base;
|
|
223
|
+
return this.containerName(parts, typeIndex, DEFAULT_CONTAINER_DEPTH);
|
|
224
|
+
}
|
|
225
|
+
// inFolder is checked before the container check because a type can be
|
|
226
|
+
// both inFolder and a content container (Document): adapter-first would
|
|
227
|
+
// truncate every Document to its folder, discarding the varying-extension
|
|
228
|
+
// files that folder owns.
|
|
229
|
+
componentScopedName(path, type) {
|
|
230
|
+
const parts = path.split(PATH_SEP);
|
|
231
|
+
const typeIndex = parts.lastIndexOf(type.directoryName);
|
|
232
|
+
if (typeIndex === -1)
|
|
233
|
+
return parse(path).base;
|
|
234
|
+
if (type.inFolder)
|
|
235
|
+
return this.folderScopedName(parts, typeIndex);
|
|
236
|
+
if (this.isContentContainer(type)) {
|
|
237
|
+
const nestedCount = parts.length - typeIndex - 1;
|
|
238
|
+
const depth = this.containerDepth(type.adapter, nestedCount);
|
|
239
|
+
return this.containerName(parts, typeIndex, depth);
|
|
153
240
|
}
|
|
154
|
-
return
|
|
241
|
+
return this.nestedContentName(parts, typeIndex, type);
|
|
242
|
+
}
|
|
243
|
+
// The `-meta.xml` companion sits outside the extension
|
|
244
|
+
// (`logo.png-meta.xml`), so it must fall away before the extension strip
|
|
245
|
+
// runs — the reverse order would leave it attached and never removed.
|
|
246
|
+
// The trailing `Folder` strip mirrors the folder handler's own name
|
|
247
|
+
// derivation, so the key cannot disagree with the descriptor for a name
|
|
248
|
+
// that happens to end in Folder.
|
|
249
|
+
folderScopedName(parts, typeIndex) {
|
|
250
|
+
return withoutMetaSuffix(parts.slice(typeIndex).join(PATH_SEP))
|
|
251
|
+
.replace(INFOLDER_SUFFIX_REGEX, '')
|
|
252
|
+
.replace(EXTENSION_SUFFIX_REGEX, '');
|
|
253
|
+
}
|
|
254
|
+
// A flat nested-content directory tells its families apart by extension
|
|
255
|
+
// (`wave/A.wdash` vs `wave/A.wapp`), so the extension is kept and any
|
|
256
|
+
// sub-directory is discarded, matching the descriptor. A
|
|
257
|
+
// FOLDER_SCOPED_TYPES member (VirtualBot) is instead named
|
|
258
|
+
// `<bot>.<version>` and needs every segment from the type directory down.
|
|
259
|
+
nestedContentName(parts, typeIndex, type) {
|
|
260
|
+
const tail = MetadataRepositoryImpl.FOLDER_SCOPED_TYPES.has(type.xmlName)
|
|
261
|
+
? parts.slice(typeIndex)
|
|
262
|
+
: [parts[typeIndex], parts.at(-1)];
|
|
263
|
+
return tail.join(PATH_SEP).replace(META_REGEX, '');
|
|
264
|
+
}
|
|
265
|
+
// A deeper remainder means the container is named by a directory, and a
|
|
266
|
+
// directory name is the component's whole name — stripping it would
|
|
267
|
+
// corrupt one that merely contains a dot (`lwc/foo.bar/…`). Only when the
|
|
268
|
+
// taken segments consume the whole remainder is the name actually a file,
|
|
269
|
+
// and only then are `-meta.xml` and the extension noise to strip away.
|
|
270
|
+
containerName(parts, typeIndex, depth) {
|
|
271
|
+
const nested = parts.slice(typeIndex + 1);
|
|
272
|
+
const name = [parts[typeIndex], ...nested.slice(0, depth)].join(PATH_SEP);
|
|
273
|
+
return depth >= nested.length
|
|
274
|
+
? withoutMetaSuffix(name).replace(EXTENSION_SUFFIX_REGEX, '')
|
|
275
|
+
: name;
|
|
276
|
+
}
|
|
277
|
+
// Only digitalExperience varies by depth: a page content file lives
|
|
278
|
+
// DIGITAL_EXPERIENCE_CONTENT_DEPTH segments below the bundle directory,
|
|
279
|
+
// while the bundle itself and any shallower/non-canonical layout share the
|
|
280
|
+
// coarser DIGITAL_EXPERIENCE_BUNDLE_DEPTH. Every other content-container
|
|
281
|
+
// adapter names its component by the single segment below its directory.
|
|
282
|
+
containerDepth(adapter, nestedCount) {
|
|
283
|
+
if (adapter !== DIGITAL_EXPERIENCE_ADAPTER)
|
|
284
|
+
return DEFAULT_CONTAINER_DEPTH;
|
|
285
|
+
return nestedCount > DIGITAL_EXPERIENCE_CONTENT_DEPTH
|
|
286
|
+
? DIGITAL_EXPERIENCE_CONTENT_DEPTH
|
|
287
|
+
: DIGITAL_EXPERIENCE_BUNDLE_DEPTH;
|
|
155
288
|
}
|
|
156
289
|
values() {
|
|
157
290
|
return this.metadatas;
|
|
158
291
|
}
|
|
159
|
-
static UNSAFE_EXTENSION = new Set([
|
|
160
|
-
CUSTOM_APPLICATION_SUFFIX,
|
|
161
|
-
EMAIL_SERVICES_FUNCTION_SUFFIX,
|
|
162
|
-
CUSTOM_METADATA_SUFFIX,
|
|
163
|
-
]);
|
|
164
292
|
static COMPOSED_TYPES = new Set([
|
|
165
293
|
OBJECT_TYPE,
|
|
166
294
|
OBJECT_TRANSLATION_TYPE,
|
|
@@ -169,6 +297,20 @@ export class MetadataRepositoryImpl {
|
|
|
169
297
|
SHARING_RULE_TYPE,
|
|
170
298
|
...SUB_OBJECT_TYPES,
|
|
171
299
|
]);
|
|
300
|
+
// A subset of COMPOSED_TYPES: PermissionSet and CustomObjectTranslation
|
|
301
|
+
// decompose into per-file children like every other composed type, but
|
|
302
|
+
// (unlike CustomObject/Workflow/SharingRules) their children are not
|
|
303
|
+
// distinct components — every file under one holder is the same
|
|
304
|
+
// PermissionSet or CustomObjectTranslation, so they key on the holder
|
|
305
|
+
// instead of the decomposed path.
|
|
306
|
+
static HOLDER_SCOPED_COMPOSED_TYPES = new Set([
|
|
307
|
+
PERMISSIONSET_TYPE,
|
|
308
|
+
OBJECT_TRANSLATION_TYPE,
|
|
309
|
+
]);
|
|
310
|
+
// Nested-content families sharing one flat directory are told apart by
|
|
311
|
+
// extension, except VirtualBot: a BotVersion member is `<bot>.<version>`,
|
|
312
|
+
// so its key must keep the bot folder that the extension alone would lose.
|
|
313
|
+
static FOLDER_SCOPED_TYPES = new Set([VIRTUAL_BOT_TYPE]);
|
|
172
314
|
}
|
|
173
315
|
__decorate([
|
|
174
316
|
log
|