Skip to content

Commit aad49ae

Browse files
committed
vscode-extension: when auto-populating the launch configuration, handle "extends" field in asconfig.json and merge all files (references BowlerHatLLC/vscode-as3mxml#829)
Fixes errors when certain fields are only in the base asconfig, like this one: > Missing "program" path for SWF debug configuration. Must be a .swf file or an Adobe AIR application descriptor.
1 parent cb06ec1 commit aad49ae

File tree

1 file changed

+319
-13
lines changed

1 file changed

+319
-13
lines changed

vscode-extension/src/main/ts/utils/SWFDebugConfigurationProvider.ts

Lines changed: 319 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ const CONFIG_AIRMOBILE = "airmobile";
3232

3333
const PROFILE_MOBILE_DEVICE = "mobileDevice";
3434

35+
const AIR_PLATFORM_TYPES = [
36+
"air",
37+
"ios",
38+
"ios_simulator",
39+
"android",
40+
"windows",
41+
"mac",
42+
"linux",
43+
];
44+
3545
interface SWFDebugConfiguration extends vscode.DebugConfiguration {
3646
program?: string;
3747
profile?: string;
@@ -129,24 +139,48 @@ export default class SWFDebugConfigurationProvider
129139
}
130140
asconfigPath = asconfigPathParts.slice(1).join(path.sep);
131141
}
132-
let asconfigJSON: any = null;
142+
let asconfigJSON: any | null = null;
133143
if (workspaceFolder !== undefined) {
134144
asconfigPath ??= FILE_NAME_ASCONFIG_JSON;
135-
if (asconfigPath && !path.isAbsolute(asconfigPath)) {
145+
if (!path.isAbsolute(asconfigPath)) {
136146
asconfigPath = path.resolve(workspaceFolder.uri.fsPath, asconfigPath);
137147
}
138-
if (asconfigPath && fs.existsSync(asconfigPath)) {
139-
try {
140-
let asconfigFile = fs.readFileSync(asconfigPath, "utf8");
141-
asconfigJSON = json5.parse(asconfigFile);
142-
} catch (error) {
143-
//something went terribly wrong!
144-
vscode.window.showErrorMessage(
145-
`Failed to debug SWF. Error reading file: ${asconfigPath}`
146-
);
147-
console.error(error);
148-
return undefined;
148+
let currentAsconfigPath: string | null = asconfigPath;
149+
while (currentAsconfigPath) {
150+
if (fs.existsSync(currentAsconfigPath)) {
151+
try {
152+
const asconfigFile: string = fs.readFileSync(
153+
currentAsconfigPath,
154+
"utf8"
155+
);
156+
const parsedAsconfigFile: any = json5.parse(asconfigFile);
157+
if (asconfigJSON) {
158+
asconfigJSON = mergeConfigs(asconfigJSON, parsedAsconfigFile);
159+
} else {
160+
asconfigJSON = parsedAsconfigFile;
161+
}
162+
if (parsedAsconfigFile.extends) {
163+
currentAsconfigPath = parsedAsconfigFile.extends;
164+
if (currentAsconfigPath) {
165+
if (!path.isAbsolute(currentAsconfigPath)) {
166+
currentAsconfigPath = path.resolve(
167+
workspaceFolder.uri.fsPath,
168+
currentAsconfigPath
169+
);
170+
}
171+
continue;
172+
}
173+
}
174+
} catch (error) {
175+
//something went terribly wrong!
176+
vscode.window.showErrorMessage(
177+
`Failed to debug SWF. Error reading file: ${currentAsconfigPath}`
178+
);
179+
console.error(error);
180+
return undefined;
181+
}
149182
}
183+
currentAsconfigPath = null;
150184
}
151185
}
152186
if (!asconfigPath) {
@@ -644,3 +678,275 @@ function findApplicationID(appDescriptorContent: string): string | undefined {
644678
}
645679
return undefined;
646680
}
681+
682+
function mergeConfigs(configData: any, baseConfigData: any): any {
683+
const result: any = {};
684+
const keys = new Set<PropertyKey>();
685+
for (const key in baseConfigData) {
686+
if (baseConfigData.hasOwnProperty(key)) {
687+
keys.add(key);
688+
}
689+
}
690+
for (const key in configData) {
691+
if (configData.hasOwnProperty(key)) {
692+
keys.add(key);
693+
}
694+
}
695+
keys.forEach(function (
696+
value: PropertyKey,
697+
key: PropertyKey,
698+
set: Set<PropertyKey>
699+
): void {
700+
if (key === "extends") {
701+
//safe to skip
702+
return;
703+
}
704+
var hasConfig = configData.hasOwnProperty(key);
705+
var hasBase = baseConfigData.hasOwnProperty(key);
706+
if (hasConfig && hasBase) {
707+
if (key === "application") {
708+
result[key] = mergeApplication(configData[key], baseConfigData[key]);
709+
} else if (key === "compilerOptions") {
710+
result[key] = mergeCompilerOptions(
711+
configData[key],
712+
baseConfigData[key]
713+
);
714+
} else if (key === "airOptions") {
715+
result[key] = mergeAIROptions(
716+
configData[key],
717+
baseConfigData[key],
718+
true
719+
);
720+
} else {
721+
result[key] = mergeObjectsSimple(configData[key], baseConfigData[key]);
722+
}
723+
} else if (hasConfig) {
724+
result[key] = configData[key];
725+
} else if (hasBase) {
726+
result[key] = baseConfigData[key];
727+
}
728+
});
729+
return result;
730+
}
731+
732+
function mergeObjectsSimple(object: any, baseObject: any): any {
733+
if (typeof object !== "object" || Array.isArray(object)) {
734+
return object;
735+
}
736+
var result: any = {};
737+
for (const key in baseObject) {
738+
if (baseObject.hasOwnProperty(key)) {
739+
result[key] = baseObject[key];
740+
}
741+
}
742+
for (const key in object) {
743+
if (object.hasOwnProperty(key)) {
744+
result[key] = object[key];
745+
}
746+
}
747+
return result;
748+
}
749+
750+
function mergeArrays(array: any[], baseArray: any[]): any[] {
751+
const result: any[] = baseArray.slice();
752+
for (const item of array) {
753+
const isObject = typeof item === "object";
754+
if (
755+
!result.find(function (existingItem: any): boolean {
756+
if (isObject) {
757+
for (const key in existingItem) {
758+
if (!(key in item) || item[key] !== existingItem[key]) {
759+
return false;
760+
}
761+
}
762+
for (const key in item) {
763+
if (!(key in existingItem) || item[key] !== existingItem[key]) {
764+
return false;
765+
}
766+
}
767+
return true;
768+
}
769+
return item === existingItem;
770+
})
771+
) {
772+
result.push(item);
773+
}
774+
}
775+
return result;
776+
}
777+
778+
function mergeArrayWithComparisonKey(
779+
array: any[],
780+
baseArray: any[],
781+
comparisonKey: string
782+
): any[] {
783+
var result: any[] = array.slice();
784+
var values = new Set<any>();
785+
for (const pair of array) {
786+
values.add(pair[comparisonKey]);
787+
}
788+
for (const pair of baseArray) {
789+
var valueToCompare: any = pair[comparisonKey];
790+
if (values.has(valueToCompare)) {
791+
//if we already added this value, skip it because its been
792+
//overridden in the main config
793+
continue;
794+
}
795+
result.push(pair);
796+
}
797+
return result;
798+
}
799+
800+
function mergeCompilerOptions(
801+
compilerOptions: any,
802+
baseCompilerOptions: any
803+
): any {
804+
const result: any = {};
805+
for (const key in baseCompilerOptions) {
806+
if (baseCompilerOptions.hasOwnProperty(key)) {
807+
result[key] = baseCompilerOptions[key];
808+
}
809+
}
810+
for (const key in compilerOptions) {
811+
if (compilerOptions.hasOwnProperty(key)) {
812+
var newValue: any = compilerOptions[key];
813+
if (key === "define") {
814+
if (result.hasOwnProperty(key)) {
815+
var oldDefine: any = result[key];
816+
result[key] = mergeArrayWithComparisonKey(
817+
newValue as any[],
818+
oldDefine as any[],
819+
"name"
820+
);
821+
} else {
822+
result[key] = newValue;
823+
}
824+
} else if (Array.isArray(newValue)) {
825+
if (result.hasOwnProperty(key)) {
826+
var oldArray: any = result[key];
827+
if (Array.isArray(oldArray)) {
828+
result[key] = mergeArrays(newValue as any[], oldArray as any[]);
829+
} else {
830+
result[key] = newValue;
831+
}
832+
} else {
833+
result[key] = newValue;
834+
}
835+
} else {
836+
result[key] = newValue;
837+
}
838+
}
839+
}
840+
return result;
841+
}
842+
843+
function mergeApplication(application: any, baseApplication: any): any {
844+
if (typeof application === "string") {
845+
//overrides all fields
846+
return application;
847+
}
848+
var result: any = {};
849+
if (typeof baseApplication === "string") {
850+
for (const key in AIR_PLATFORM_TYPES) {
851+
if (AIR_PLATFORM_TYPES.hasOwnProperty(key)) {
852+
var keyValue = AIR_PLATFORM_TYPES[key];
853+
result[keyValue] = baseApplication;
854+
}
855+
}
856+
} else {
857+
result = baseApplication;
858+
}
859+
return mergeObjectsSimple(application, result);
860+
}
861+
862+
function mergeAIROptions(
863+
airOptions: any,
864+
baseAIROptions: any,
865+
handlePlatforms: boolean
866+
): any {
867+
const result: any = {};
868+
const keys = new Set<PropertyKey>();
869+
for (const key in baseAIROptions) {
870+
if (baseAIROptions.hasOwnProperty(key)) {
871+
keys.add(key);
872+
}
873+
}
874+
for (const key in airOptions) {
875+
if (airOptions.hasOwnProperty(key)) {
876+
keys.add(key);
877+
}
878+
}
879+
var platforms = new Set();
880+
for (const key in AIR_PLATFORM_TYPES) {
881+
if (AIR_PLATFORM_TYPES.hasOwnProperty(key)) {
882+
var keyValue = AIR_PLATFORM_TYPES[key];
883+
platforms.add(keyValue);
884+
}
885+
}
886+
keys.forEach(function (
887+
value: PropertyKey,
888+
key: PropertyKey,
889+
set: Set<PropertyKey>
890+
): void {
891+
var hasConfig: Boolean = airOptions.hasOwnProperty(key);
892+
var hasBase: Boolean = baseAIROptions.hasOwnProperty(key);
893+
if (hasConfig && hasBase) {
894+
var newValue: any = airOptions[key];
895+
var baseValue: any = baseAIROptions[key];
896+
if (handlePlatforms && platforms.has(key)) {
897+
result[key] = mergeAIROptions(newValue, baseValue, false);
898+
} else if (key === "files") {
899+
result[key] = mergeArrayWithComparisonKey(
900+
newValue as any[],
901+
baseValue as any[],
902+
"path"
903+
);
904+
} else if (key === "signingOptions") {
905+
result[key] = mergeSigningOptions(newValue, baseValue);
906+
} else if (Array.isArray(newValue) && Array.isArray(baseValue)) {
907+
result[key] = mergeArrays(newValue as any[], baseValue as any[]);
908+
} else {
909+
result[key] = mergeObjectsSimple(airOptions[key], baseAIROptions[key]);
910+
}
911+
} else if (hasConfig) {
912+
result[key] = airOptions[key];
913+
} else if (hasBase) {
914+
result[key] = baseAIROptions[key];
915+
}
916+
});
917+
return result;
918+
}
919+
920+
function mergeSigningOptions(
921+
signingOptions: any,
922+
baseSigningOptions: any
923+
): any {
924+
const hasDebug = signingOptions.hasOwnProperty("debug");
925+
const hasRelease = signingOptions.hasOwnProperty("release");
926+
if (!hasDebug && !hasRelease) {
927+
//nothing to merge. fully overrides the base
928+
return signingOptions;
929+
}
930+
if (hasDebug && hasRelease) {
931+
//also fully overrides the base
932+
return signingOptions;
933+
}
934+
const hasBaseDebug: Boolean = baseSigningOptions.hasOwnProperty("debug");
935+
const hasBaseRelease: Boolean = baseSigningOptions.hasOwnProperty("release");
936+
const result: any = {};
937+
if (hasDebug) {
938+
result["debug"] = signingOptions["debug"];
939+
} else if (hasBaseDebug) {
940+
result["debug"] = baseSigningOptions["debug"];
941+
} else if (!hasBaseRelease) {
942+
result["debug"] = baseSigningOptions;
943+
}
944+
if (hasRelease) {
945+
result["release"] = signingOptions["release"];
946+
} else if (hasBaseRelease) {
947+
result["release"] = baseSigningOptions["release"];
948+
} else if (!hasBaseDebug) {
949+
result["release"] = baseSigningOptions;
950+
}
951+
return result;
952+
}

0 commit comments

Comments
 (0)