-
Notifications
You must be signed in to change notification settings - Fork 143
feat: web compatibility W-20175875 #1650
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8c5b9fd
810a95a
5881c94
9df3d5a
616fe75
430992f
cf4defc
c2f10d0
f35cef0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,8 @@ | |
| import { basename, dirname, extname, join, posix, sep } from 'node:path'; | ||
| import { SfError } from '@salesforce/core/sfError'; | ||
| import { ensureArray } from '@salesforce/kit'; | ||
| import { ComponentLike, SourceComponent } from '../resolve'; | ||
| import { SourceComponentWithContent, SourceComponent } from '../resolve/sourceComponent'; | ||
| import { ComponentLike } from '../resolve'; | ||
| import { registry } from '../registry/registry'; | ||
| import { | ||
| BooleanString, | ||
|
|
@@ -29,6 +30,7 @@ import { | |
| MetadataApiDeployStatus, | ||
| } from './types'; | ||
| import { parseDeployDiagnostic } from './diagnosticUtil'; | ||
| import { isWebAppBundle } from './utils'; | ||
|
|
||
| type DeployMessageWithComponentType = DeployMessage & { componentType: string }; | ||
| /** | ||
|
|
@@ -79,50 +81,57 @@ const shouldWalkContent = (component: SourceComponent): boolean => | |
| (t) => t.unaddressableWithoutParent === true || t.isAddressable === false | ||
| )); | ||
|
|
||
| export const createResponses = (component: SourceComponent, responseMessages: DeployMessage[]): FileResponse[] => | ||
| responseMessages.flatMap((message): FileResponse[] => { | ||
| const state = getState(message); | ||
| const base = { fullName: component.fullName, type: component.type.name } as const; | ||
|
|
||
| if (state === ComponentStatus.Failed) { | ||
| return [{ ...base, state, ...parseDeployDiagnostic(component, message) } satisfies FileResponseFailure]; | ||
| } else { | ||
| const isWebAppBundle = | ||
| component.type.name === 'DigitalExperienceBundle' && | ||
| component.fullName.startsWith('web_app/') && | ||
| component.content; | ||
|
|
||
| if (isWebAppBundle) { | ||
| const walkedPaths = component.walkContent(); | ||
| const bundleResponse: FileResponseSuccess = { | ||
| fullName: component.fullName, | ||
| type: component.type.name, | ||
| state, | ||
| filePath: component.content!, | ||
| }; | ||
| const fileResponses: FileResponseSuccess[] = walkedPaths.map((filePath) => { | ||
| // Normalize paths to ensure relative() works correctly on Windows | ||
| const normalizedContent = component.content!.split(sep).join(posix.sep); | ||
| const normalizedFilePath = filePath.split(sep).join(posix.sep); | ||
| const relPath = posix.relative(normalizedContent, normalizedFilePath); | ||
| return { | ||
| fullName: posix.join(component.fullName, relPath), | ||
| type: 'DigitalExperience', | ||
| state, | ||
| filePath, | ||
| }; | ||
| }); | ||
| return [bundleResponse, ...fileResponses]; | ||
| export const createResponses = | ||
| (projectPath?: string) => | ||
| (component: SourceComponent, responseMessages: DeployMessage[]): FileResponse[] => | ||
| responseMessages.flatMap((message): FileResponse[] => { | ||
| const state = getState(message); | ||
| const base = { fullName: component.fullName, type: component.type.name } as const; | ||
|
|
||
| if (state === ComponentStatus.Failed) { | ||
| return [{ ...base, state, ...parseDeployDiagnostic(component, message) } satisfies FileResponseFailure]; | ||
| } | ||
|
|
||
| return [ | ||
| ...(shouldWalkContent(component) | ||
| ? component.walkContent().map((filePath): FileResponseSuccess => ({ ...base, state, filePath })) | ||
| : []), | ||
| ...(component.xml ? [{ ...base, state, filePath: component.xml } satisfies FileResponseSuccess] : []), | ||
| ]; | ||
| } | ||
| }); | ||
| return ( | ||
| isWebAppBundle(component) | ||
| ? [ | ||
| { | ||
| ...base, | ||
| state, | ||
| filePath: component.content, | ||
| }, | ||
| ...component.walkContent().map((filePath) => ({ | ||
| fullName: getWebAppBundleContentFullName(component)(filePath), | ||
| type: 'DigitalExperience', | ||
| state, | ||
| filePath, | ||
| })), | ||
| ] | ||
| : [ | ||
| ...(shouldWalkContent(component) | ||
| ? component.walkContent().map((filePath): FileResponseSuccess => ({ ...base, state, filePath })) | ||
| : []), | ||
| ...(component.xml ? [{ ...base, state, filePath: component.xml }] : []), | ||
| ] | ||
| ).map((response) => ({ | ||
| ...response, | ||
| filePath: | ||
| // deployResults will produce filePaths relative to cwd, which might not be set in all environments | ||
| // if our CS had a projectDir set, we'll make the results relative to that path | ||
| projectPath && process.cwd() === projectPath ? response.filePath : join(projectPath ?? '', response.filePath), | ||
| })) satisfies FileResponseSuccess[]; | ||
| }); | ||
|
|
||
| const getWebAppBundleContentFullName = | ||
| (component: SourceComponentWithContent) => | ||
| (filePath: string): string => { | ||
| // Normalize paths to ensure relative() works correctly on Windows | ||
| const normalizedContent = component.content.split(sep).join(posix.sep); | ||
| const normalizedFilePath = filePath.split(sep).join(posix.sep); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. took me a while of playing with it, but since both of these are relative, and then posix.relative is between them, then I think it should be ok even without cwd?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds right to me? |
||
| const relPath = posix.relative(normalizedContent, normalizedFilePath); | ||
| return posix.join(component.fullName, relPath); | ||
| }; | ||
|
|
||
| /** | ||
| * Groups messages from the deploy result by component fullName and type | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* | ||
| * Copyright 2025, Salesforce, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| import { SourceComponent, SourceComponentWithContent } from '../resolve/sourceComponent'; | ||
|
|
||
| export const isWebAppBundle = (component: SourceComponent): component is SourceComponentWithContent => | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was repeated in a few places. made it a TS guard to help stuff downstream of the guard not have to barbarically assert |
||
| component.type.name === 'DigitalExperienceBundle' && | ||
| component.fullName.startsWith('web_app/') && | ||
| typeof component.content === 'string'; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SourceComponentWithContent passed the rule of 3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rule of 3 in this context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the same code is in 3 places, make it shared/reusable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so now there's SouceComponentWithContent instead of several places making
SourceComponent & {content: string}