Skip to content

Commit 346f2a2

Browse files
committed
Switch back to use YAML instead of JS-YAML #5081
Fixes: #5081 Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
1 parent 844cf8c commit 346f2a2

19 files changed

Lines changed: 73 additions & 58 deletions

package-lock.json

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@
148148
"got": "^11.8.6",
149149
"hasha": "^5.2.2",
150150
"istanbul": "^0.4.5",
151-
"js-yaml": "^4.1.0",
152151
"json-schema": "^0.4.0",
153152
"json-to-ast": "^2.1.0",
154153
"leasot": "^14.4.0",

src/k8s/vfs/kuberesources.utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
*-----------------------------------------------------------------------------------------------*/
55

66
import $RefParser from '@apidevtools/json-schema-ref-parser';
7-
import * as JSYAML from 'js-yaml';
87
import { findNodeAtLocation, getNodeValue, Node, parseTree } from 'jsonc-parser';
98
import * as _ from 'lodash';
109
import { Diagnostic, DiagnosticSeverity, FileStat, FileType, Range, TextDocument, Uri, workspace } from 'vscode';
11-
import { Document, isMap, isPair, isScalar, isSeq, Pair, ParsedNode, parseDocument } from 'yaml';
10+
import { Document, isMap, isPair, isScalar, isSeq, Pair, parse, ParsedNode, parseDocument, stringify } from 'yaml';
1211
import { CommandOption, CommandText } from '../../base/command';
1312
import { CliChannel, ExecutionContext } from '../../cli';
1413
import { Oc } from '../../oc/ocWrapper';
14+
import { YAML_STRINGIFY_OPTIONS } from '../../util/utils';
1515

1616
export const K8S_RESOURCE_SCHEME = 'osmsx'; // Changed from 'k8smsx' to 'osmsx' to not make a conflict with k8s extension
1717
export const K8S_RESOURCE_SCHEME_READONLY = 'osmsxro'; // Changed from 'k8smsxro' to 'osmsxro' to not make a conflict with k8s extension
@@ -95,7 +95,7 @@ export function getK8sResourceObject(document: string): K8sResource {
9595
}
9696

9797
// If not JSON or not parsed - try parcing as YAML
98-
try { return JSYAML.load(document) as K8sResource; } catch { /* ignore */ }
98+
try { return parse(document) as K8sResource; } catch { /* ignore */ }
9999

100100
return {} as K8sResource; // Never return 'undefined'
101101
}
@@ -393,7 +393,7 @@ export class Neater {
393393

394394
public static safeLoadYaml(raw: string): string | undefined {
395395
try {
396-
return JSYAML.load(raw);
396+
return parse(raw);
397397
} catch {
398398
// Ignore
399399
}
@@ -464,7 +464,7 @@ export class Neater {
464464
*/
465465
public static async neat(raw: string, executionContext?: ExecutionContext): Promise<string> {
466466
const resource = getK8sResourceObject(raw);
467-
return isK8sResource(resource) ? JSYAML.dump(await Neater.neatK8sResource(resource, executionContext)) : raw;
467+
return isK8sResource(resource) ? stringify(await Neater.neatK8sResource(resource, executionContext), YAML_STRINGIFY_OPTIONS) : raw;
468468
}
469469
}
470470

src/k8s/vfs/yaml-locator.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
55

6-
import * as vscode from 'vscode';
7-
8-
import * as YAML from 'js-yaml';
96
import { findNodeAtPosition, parse } from 'node-yaml-parser';
7+
import * as vscode from 'vscode';
8+
import { parse as yamlParse } from 'yaml';
109
import { MappingItem, Node, NodeProvider } from './locator-util';
1110

1211
export function isMapping(node: YamlNode): node is YamlMap {
@@ -112,7 +111,7 @@ export class YamlLocator {
112111
// the document and line lengths from parse method is cached into YamlCachedDocuments to avoid duplicate
113112
// parse against the same text.
114113
try {
115-
YAML.load(textDocument.getText()); // this is used to detect errors in the yaml file before actually parse it with the node-yaml-parser lib
114+
yamlParse(textDocument.getText()); // this is used to detect errors in the yaml file before actually parse it with the node-yaml-parser lib
116115
const { documents, lineLengths } = parse(textDocument.getText());
117116
this.cache[key].yamlDocs = documents;
118117
this.cache[key].lineLengths = lineLengths;

src/odo/odoPreference.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
*-----------------------------------------------------------------------------------------------*/
55

66
import * as fs from 'fs/promises';
7-
import * as YAML from 'js-yaml';
87
import * as path from 'path';
8+
import { parse, stringify } from 'yaml';
99
import { TelemetryProps } from '../telemetry';
1010
import { Platform } from '../util/platform';
11+
import { YAML_STRINGIFY_OPTIONS } from '../util/utils';
1112
import { Registry } from './componentType';
1213

1314

@@ -117,7 +118,7 @@ export class OdoPreference {
117118
let isToBeReWritten: boolean = false;
118119
try {
119120
const odoPreferenceFile = await fs.readFile(odoPreferenceFilePath, 'utf8');
120-
const odoPreferences = YAML.load(odoPreferenceFile) as OdoPreferenceObject;
121+
const odoPreferences = parse(odoPreferenceFile) as OdoPreferenceObject;
121122

122123
// If `odoPreferences.OdoSettings.RegistryList` is `null` or doesn't contain the `DefaultDevfileRegistry` item
123124
// we have to recover at least the 'DefaultDevfileRegistry` item on it because this whole list will be replaced
@@ -157,7 +158,7 @@ export class OdoPreference {
157158
private async writeOdoPreference(preference: any): Promise<any> {
158159
const odoPreferenceFilePath = this.getOdoPreferenceFile();
159160
try {
160-
const preferenceYaml = YAML.dump(preference);
161+
const preferenceYaml = stringify(preference, YAML_STRINGIFY_OPTIONS);
161162
const odoPreferenceDir = path.parse(odoPreferenceFilePath).dir;
162163
if (!await this.dirExists(odoPreferenceDir)) {
163164
await fs.mkdir(odoPreferenceDir, { recursive: true, mode: 0o750} );

src/serverlessFunction/commands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*-----------------------------------------------------------------------------------------------*/
55

66
import * as fs from 'fs/promises';
7-
import { load } from 'js-yaml';
87
import * as path from 'path';
8+
import { parse } from 'yaml';
99
import { CommandOption, CommandText } from '../base/command';
1010
import { GitModel } from './git/git';
1111
import { FunctionContent, InvokeFunction } from './types';
@@ -15,7 +15,7 @@ export class Utils {
1515
let funcData: FunctionContent;
1616
try {
1717
const funcYaml: string = await fs.readFile(path.join(dir, 'func.yaml'), 'utf-8');
18-
funcData = load(funcYaml) as FunctionContent;
18+
funcData = parse(funcYaml) as FunctionContent;
1919
} catch {
2020
// ignore
2121
}

src/util/kubeUtils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import { KubeConfig, loadYaml } from '@kubernetes/client-node';
77
import { ActionOnInvalid, Cluster, Context, User } from '@kubernetes/client-node/dist/config_types';
88
import * as fs from 'fs';
9-
import { dump } from 'js-yaml';
109
import * as path from 'path';
1110
import { QuickPickItem, window } from 'vscode';
11+
import { stringify } from 'yaml';
1212
import { CommandText } from '../base/command';
1313
import { CliChannel, ExecutionContext } from '../cli';
1414
import { Platform } from './platform';
15+
import { YAML_STRINGIFY_OPTIONS } from './utils';
1516

1617
function fileExists(file: string): boolean {
1718
try {
@@ -361,7 +362,7 @@ export class KubeConfigInfo {
361362
}
362363

363364
public dumpEffectiveKubeConfig(): string {
364-
return dump(this.sanitizeKubeConfig(this.getEffectiveKubeConfig()), { sortKeys: true, lineWidth: -1 });
365+
return stringify(this.sanitizeKubeConfig(this.getEffectiveKubeConfig()), YAML_STRINGIFY_OPTIONS);
365366
}
366367

367368
private sanitizeKubeConfig(config: KubeConfig): any {
@@ -491,7 +492,7 @@ export function serializeKubeConfig(kc: KubeConfig): string {
491492
users: kc?.users?.map(toKubeUser),
492493
};
493494

494-
return dump(fullConfig, { indent: 2, lineWidth: -1, styles: { '!!str': 'plain' } });
495+
return stringify(fullConfig, YAML_STRINGIFY_OPTIONS);
495496
}
496497

497498
const _kubeConfigErrorCache: Map<string, any> = new Map<string, any>(); // Cache for KC loading errors, allows to skip duplicate reporting

src/util/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { access as accessOriginal, rm as rmOriginal } from 'fs/promises';
1515
import * as path from 'path';
1616
import { decompress as decompressOriginal } from 'targz';
17+
import { CreateNodeOptions, DocumentOptions, ParseOptions, SchemaOptions, ToStringOptions } from 'yaml';
1718

1819
/**
1920
* Returns the absolute path for a specified relative image path
@@ -31,6 +32,16 @@ export function imagePath(imagePath: string): string {
3132
return path.join(baseDir, 'images', imagePath);
3233
}
3334

35+
/**
36+
* YAML serialization options tailored for Kubernetes manifests and similar configs:
37+
* - sortMapEntries: true → ensures object keys are sorted for consistency (useful for diffs)
38+
* - indent: 2 → standard YAML indentation, aligns with K8s and Devfile formatting
39+
* - lineWidth: 0 → disables line wrapping for better readability and stability in tools
40+
* - version: '1.2' → uses modern YAML 1.2 spec (default is 1.2 but made explicit here)
41+
* - directives: false → omits the '---' document start marker unless explicitly needed
42+
*/
43+
export const YAML_STRINGIFY_OPTIONS: DocumentOptions & SchemaOptions & ParseOptions & CreateNodeOptions & ToStringOptions = { sortMapEntries: true, indent: 2, lineWidth: 0, version: '1.2', directives: false }
44+
3445
// The following wrappers are needed for unit tests due to
3546
// 'TypeError: Descriptor for property XXX is non-configurable and non-writable' error
3647
// that may occure when trying to stub the following methods with SinonStub

src/webview/create-component/createComponentLoader.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import * as cp from 'child_process';
66
import * as fse from 'fs-extra';
77
import * as fs from 'fs/promises';
8-
import * as JSYAML from 'js-yaml';
98
import * as path from 'path';
109
import * as semver from 'semver';
1110
import * as tmp from 'tmp';
1211
import { promisify } from 'util';
1312
import * as vscode from 'vscode';
1413
import { extensions, Uri, ViewColumn, WebviewPanel, window } from 'vscode';
14+
import { parse, stringify } from 'yaml';
1515
import { Alizer } from '../../alizer/alizerWrapper';
1616
import { AlizerDevfileResponse, Version } from '../../alizer/types';
1717
import { DevfileInfo, DevfileInfoExt, DevfileVersionInfo } from '../../devfile-registry/devfileInfo';
@@ -23,6 +23,7 @@ import sendTelemetry from '../../telemetry';
2323
import { ExtensionID } from '../../util/constants';
2424
import { DevfileConverter } from '../../util/devfileConverter';
2525
import { DevfileV1 } from '../../util/devfileV1Type';
26+
import { YAML_STRINGIFY_OPTIONS } from '../../util/utils';
2627
import { getInitialWorkspaceFolder, selectWorkspaceFolder } from '../../util/workspace';
2728
import {
2829
isValidProjectFolder,
@@ -474,11 +475,11 @@ export default class CreateComponentLoader {
474475
static async updateDevfileWithComponentName(ucomponentFolderUri: vscode.Uri, componentName: string): Promise<void> {
475476
const devFilePath = path.join(ucomponentFolderUri.fsPath, 'devfile.yaml');
476477
const file = await fs.readFile(devFilePath, 'utf8');
477-
const devfile = JSYAML.load(file.toString()) as any;
478+
const devfile = parse(file.toString());
478479
if (devfile?.metadata?.name !== componentName) {
479480
devfile.metadata.name = componentName;
480481
await fs.unlink(devFilePath);
481-
const yaml = JSYAML.dump(devfile, { sortKeys: true });
482+
const yaml = stringify(devfile, YAML_STRINGIFY_OPTIONS);
482483
await fs.writeFile(devFilePath, yaml.toString(), 'utf-8');
483484
}
484485
}
@@ -504,7 +505,7 @@ export default class CreateComponentLoader {
504505
//Try reading the raw devfile
505506
const devFileYamlPath = path.join(tmpFolder.fsPath, 'devfile.yaml');
506507
const file = await fs.readFile(devFileYamlPath, 'utf8');
507-
rawDevfile = JSYAML.load(file.toString());
508+
rawDevfile = parse(file.toString());
508509
}
509510

510511
void CreateComponentLoader.panel.webview.postMessage({
@@ -517,7 +518,7 @@ export default class CreateComponentLoader {
517518
id: rawDevfile.metadata.name,
518519
starterProjects: rawDevfile.starterProjects,
519520
tags: [],
520-
yaml: JSYAML.dump(rawDevfile),
521+
yaml: stringify(rawDevfile, YAML_STRINGIFY_OPTIONS),
521522
supportsDebug,
522523
supportsDeploy,
523524
} as Devfile;
@@ -551,7 +552,7 @@ export default class CreateComponentLoader {
551552
try {
552553
const devFileV1Path = path.join(uri.fsPath, 'devfile.yaml');
553554
const file = await fs.readFile(devFileV1Path, 'utf8');
554-
const devfileV1 = JSYAML.load(file.toString()) as DevfileV1;
555+
const devfileV1 = parse(file.toString()) as DevfileV1;
555556
await fs.unlink(devFileV1Path);
556557
analyzeRes = await Alizer.Instance.alizerDevfile(uri);
557558
compDescriptions = await getCompDescriptionsAfterAnalizer(analyzeRes);
@@ -560,7 +561,7 @@ export default class CreateComponentLoader {
560561
devfileV1,
561562
endPoints,
562563
);
563-
const yaml = JSYAML.dump(devfileV2, { sortKeys: true });
564+
const yaml = stringify(devfileV2, YAML_STRINGIFY_OPTIONS);
564565
await fs.writeFile(devFileV1Path, yaml.toString(), 'utf-8');
565566
await CreateComponentLoader.panel?.webview.postMessage({
566567
action: 'devfileRegenerated',

src/webview/create-service/createServiceViewLoader.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
55
import { randomInt } from 'crypto';
6-
import { dump as dumpYaml, load as loadYaml } from 'js-yaml';
76
import { JSONSchema7 } from 'json-schema';
87
import * as _ from 'lodash';
98
import * as path from 'path';
109
import * as vscode from 'vscode';
10+
import { parse, stringify } from 'yaml';
1111
import { OpenShiftExplorer } from '../../explorer';
1212
import { ClusterServiceVersionKind, CustomResourceDefinitionKind } from '../../k8s/olm/types';
1313
import { Oc } from '../../oc/ocWrapper';
1414
import { getServiceKindStubs } from '../../openshift/serviceHelpers';
1515
import { ExtensionID } from '../../util/constants';
16+
import { YAML_STRINGIFY_OPTIONS } from '../../util/utils';
1617
import { loadWebviewHtml } from '../common-ext/utils';
1718
import type {
1819
CustomResourceDefinitionStub,
@@ -122,7 +123,7 @@ export default class CreateServiceViewLoader {
122123
});
123124
} else {
124125
// create a new unsaved YAML file with the default data and show it to the user
125-
const serviceYaml = dumpYaml(defaults);
126+
const serviceYaml = stringify(defaults, YAML_STRINGIFY_OPTIONS);
126127
const newTextDocument = await vscode.workspace.openTextDocument({ content: serviceYaml, language: 'yaml' });
127128
await vscode.window.showTextDocument(newTextDocument);
128129

@@ -200,7 +201,7 @@ async function getDefaultsFromServiceKindStub(clusterServiceVersion: ClusterServ
200201
};
201202
const examplesYaml: string =
202203
clusterServiceVersion.metadata?.annotations?.['alm-examples'];
203-
const examples: any[] = examplesYaml ? loadYaml(examplesYaml) as any[] : [];
204+
const examples: any[] = examplesYaml ? parse(examplesYaml) as any[] : [];
204205
const example = examples.find((item) => item.kind === stub.kind);
205206
if (example) {
206207
defaults = _.merge(example, defaults);

0 commit comments

Comments
 (0)