Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/lib/policies.js
3520 views
1
"use strict";
2
var __importDefault = (this && this.__importDefault) || function (mod) {
3
return (mod && mod.__esModule) ? mod : { "default": mod };
4
};
5
Object.defineProperty(exports, "__esModule", { value: true });
6
/*---------------------------------------------------------------------------------------------
7
* Copyright (c) Microsoft Corporation. All rights reserved.
8
* Licensed under the MIT License. See License.txt in the project root for license information.
9
*--------------------------------------------------------------------------------------------*/
10
const child_process_1 = require("child_process");
11
const fs_1 = require("fs");
12
const path_1 = __importDefault(require("path"));
13
const byline_1 = __importDefault(require("byline"));
14
const ripgrep_1 = require("@vscode/ripgrep");
15
const tree_sitter_1 = __importDefault(require("tree-sitter"));
16
const { typescript } = require('tree-sitter-typescript');
17
const product = require('../../product.json');
18
const packageJson = require('../../package.json');
19
function isNlsString(value) {
20
return value ? typeof value !== 'string' : false;
21
}
22
function isStringArray(value) {
23
return !value.some(s => isNlsString(s));
24
}
25
function isNlsStringArray(value) {
26
return value.every(s => isNlsString(s));
27
}
28
var PolicyType;
29
(function (PolicyType) {
30
PolicyType["Boolean"] = "boolean";
31
PolicyType["Number"] = "number";
32
PolicyType["Object"] = "object";
33
PolicyType["String"] = "string";
34
PolicyType["StringEnum"] = "stringEnum";
35
})(PolicyType || (PolicyType = {}));
36
function renderADMLString(prefix, moduleName, nlsString, translations) {
37
let value;
38
if (translations) {
39
const moduleTranslations = translations[moduleName];
40
if (moduleTranslations) {
41
value = moduleTranslations[nlsString.nlsKey];
42
}
43
}
44
if (!value) {
45
value = nlsString.value;
46
}
47
return `<string id="${prefix}_${nlsString.nlsKey.replace(/\./g, '_')}">${value}</string>`;
48
}
49
function renderProfileString(_prefix, moduleName, nlsString, translations) {
50
let value;
51
if (translations) {
52
const moduleTranslations = translations[moduleName];
53
if (moduleTranslations) {
54
value = moduleTranslations[nlsString.nlsKey];
55
}
56
}
57
if (!value) {
58
value = nlsString.value;
59
}
60
return value;
61
}
62
class BasePolicy {
63
type;
64
name;
65
category;
66
minimumVersion;
67
description;
68
moduleName;
69
constructor(type, name, category, minimumVersion, description, moduleName) {
70
this.type = type;
71
this.name = name;
72
this.category = category;
73
this.minimumVersion = minimumVersion;
74
this.description = description;
75
this.moduleName = moduleName;
76
}
77
renderADMLString(nlsString, translations) {
78
return renderADMLString(this.name, this.moduleName, nlsString, translations);
79
}
80
renderADMX(regKey) {
81
return [
82
`<policy name="${this.name}" class="Both" displayName="$(string.${this.name})" explainText="$(string.${this.name}_${this.description.nlsKey.replace(/\./g, '_')})" key="Software\\Policies\\Microsoft\\${regKey}" presentation="$(presentation.${this.name})">`,
83
` <parentCategory ref="${this.category.name.nlsKey}" />`,
84
` <supportedOn ref="Supported_${this.minimumVersion.replace(/\./g, '_')}" />`,
85
` <elements>`,
86
...this.renderADMXElements(),
87
` </elements>`,
88
`</policy>`
89
];
90
}
91
renderADMLStrings(translations) {
92
return [
93
`<string id="${this.name}">${this.name}</string>`,
94
this.renderADMLString(this.description, translations)
95
];
96
}
97
renderADMLPresentation() {
98
return `<presentation id="${this.name}">${this.renderADMLPresentationContents()}</presentation>`;
99
}
100
renderProfile() {
101
return [`<key>${this.name}</key>`, this.renderProfileValue()];
102
}
103
renderProfileManifest(translations) {
104
return `<dict>
105
${this.renderProfileManifestValue(translations)}
106
</dict>`;
107
}
108
}
109
class BooleanPolicy extends BasePolicy {
110
static from(name, category, minimumVersion, description, moduleName, settingNode) {
111
const type = getStringProperty(moduleName, settingNode, 'type');
112
if (type !== 'boolean') {
113
return undefined;
114
}
115
return new BooleanPolicy(name, category, minimumVersion, description, moduleName);
116
}
117
constructor(name, category, minimumVersion, description, moduleName) {
118
super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName);
119
}
120
renderADMXElements() {
121
return [
122
`<boolean id="${this.name}" valueName="${this.name}">`,
123
` <trueValue><decimal value="1" /></trueValue><falseValue><decimal value="0" /></falseValue>`,
124
`</boolean>`
125
];
126
}
127
renderADMLPresentationContents() {
128
return `<checkBox refId="${this.name}">${this.name}</checkBox>`;
129
}
130
renderProfileValue() {
131
return `<false/>`;
132
}
133
renderProfileManifestValue(translations) {
134
return `<key>pfm_default</key>
135
<false/>
136
<key>pfm_description</key>
137
<string>${renderProfileString(this.name, this.moduleName, this.description, translations)}</string>
138
<key>pfm_name</key>
139
<string>${this.name}</string>
140
<key>pfm_title</key>
141
<string>${this.name}</string>
142
<key>pfm_type</key>
143
<string>boolean</string>`;
144
}
145
}
146
class ParseError extends Error {
147
constructor(message, moduleName, node) {
148
super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`);
149
}
150
}
151
class NumberPolicy extends BasePolicy {
152
defaultValue;
153
static from(name, category, minimumVersion, description, moduleName, settingNode) {
154
const type = getStringProperty(moduleName, settingNode, 'type');
155
if (type !== 'number') {
156
return undefined;
157
}
158
const defaultValue = getNumberProperty(moduleName, settingNode, 'default');
159
if (typeof defaultValue === 'undefined') {
160
throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode);
161
}
162
return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue);
163
}
164
constructor(name, category, minimumVersion, description, moduleName, defaultValue) {
165
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
166
this.defaultValue = defaultValue;
167
}
168
renderADMXElements() {
169
return [
170
`<decimal id="${this.name}" valueName="${this.name}" />`
171
// `<decimal id="Quarantine_PurgeItemsAfterDelay" valueName="PurgeItemsAfterDelay" minValue="0" maxValue="10000000" />`
172
];
173
}
174
renderADMLPresentationContents() {
175
return `<decimalTextBox refId="${this.name}" defaultValue="${this.defaultValue}">${this.name}</decimalTextBox>`;
176
}
177
renderProfileValue() {
178
return `<integer>${this.defaultValue}</integer>`;
179
}
180
renderProfileManifestValue(translations) {
181
return `<key>pfm_default</key>
182
<integer>${this.defaultValue}</integer>
183
<key>pfm_description</key>
184
<string>${renderProfileString(this.name, this.moduleName, this.description, translations)}</string>
185
<key>pfm_name</key>
186
<string>${this.name}</string>
187
<key>pfm_title</key>
188
<string>${this.name}</string>
189
<key>pfm_type</key>
190
<string>integer</string>`;
191
}
192
}
193
class StringPolicy extends BasePolicy {
194
static from(name, category, minimumVersion, description, moduleName, settingNode) {
195
const type = getStringProperty(moduleName, settingNode, 'type');
196
if (type !== 'string') {
197
return undefined;
198
}
199
return new StringPolicy(name, category, minimumVersion, description, moduleName);
200
}
201
constructor(name, category, minimumVersion, description, moduleName) {
202
super(PolicyType.String, name, category, minimumVersion, description, moduleName);
203
}
204
renderADMXElements() {
205
return [`<text id="${this.name}" valueName="${this.name}" required="true" />`];
206
}
207
renderADMLPresentationContents() {
208
return `<textBox refId="${this.name}"><label>${this.name}:</label></textBox>`;
209
}
210
renderProfileValue() {
211
return `<string></string>`;
212
}
213
renderProfileManifestValue(translations) {
214
return `<key>pfm_default</key>
215
<string></string>
216
<key>pfm_description</key>
217
<string>${renderProfileString(this.name, this.moduleName, this.description, translations)}</string>
218
<key>pfm_name</key>
219
<string>${this.name}</string>
220
<key>pfm_title</key>
221
<string>${this.name}</string>
222
<key>pfm_type</key>
223
<string>string</string>`;
224
}
225
}
226
class ObjectPolicy extends BasePolicy {
227
static from(name, category, minimumVersion, description, moduleName, settingNode) {
228
const type = getStringProperty(moduleName, settingNode, 'type');
229
if (type !== 'object' && type !== 'array') {
230
return undefined;
231
}
232
return new ObjectPolicy(name, category, minimumVersion, description, moduleName);
233
}
234
constructor(name, category, minimumVersion, description, moduleName) {
235
super(PolicyType.Object, name, category, minimumVersion, description, moduleName);
236
}
237
renderADMXElements() {
238
return [`<multiText id="${this.name}" valueName="${this.name}" required="true" />`];
239
}
240
renderADMLPresentationContents() {
241
return `<multiTextBox refId="${this.name}" />`;
242
}
243
renderProfileValue() {
244
return `<string></string>`;
245
}
246
renderProfileManifestValue(translations) {
247
return `<key>pfm_default</key>
248
<string></string>
249
<key>pfm_description</key>
250
<string>${renderProfileString(this.name, this.moduleName, this.description, translations)}</string>
251
<key>pfm_name</key>
252
<string>${this.name}</string>
253
<key>pfm_title</key>
254
<string>${this.name}</string>
255
<key>pfm_type</key>
256
<string>string</string>
257
`;
258
}
259
}
260
class StringEnumPolicy extends BasePolicy {
261
enum_;
262
enumDescriptions;
263
static from(name, category, minimumVersion, description, moduleName, settingNode) {
264
const type = getStringProperty(moduleName, settingNode, 'type');
265
if (type !== 'string') {
266
return undefined;
267
}
268
const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum');
269
if (!enum_) {
270
return undefined;
271
}
272
if (!isStringArray(enum_)) {
273
throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode);
274
}
275
const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions');
276
if (!enumDescriptions) {
277
throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode);
278
}
279
else if (!isNlsStringArray(enumDescriptions)) {
280
throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode);
281
}
282
return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions);
283
}
284
constructor(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions) {
285
super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName);
286
this.enum_ = enum_;
287
this.enumDescriptions = enumDescriptions;
288
}
289
renderADMXElements() {
290
return [
291
`<enum id="${this.name}" valueName="${this.name}">`,
292
...this.enum_.map((value, index) => ` <item displayName="$(string.${this.name}_${this.enumDescriptions[index].nlsKey})"><value><string>${value}</string></value></item>`),
293
`</enum>`
294
];
295
}
296
renderADMLStrings(translations) {
297
return [
298
...super.renderADMLStrings(translations),
299
...this.enumDescriptions.map(e => this.renderADMLString(e, translations))
300
];
301
}
302
renderADMLPresentationContents() {
303
return `<dropdownList refId="${this.name}" />`;
304
}
305
renderProfileValue() {
306
return `<string>${this.enum_[0]}</string>`;
307
}
308
renderProfileManifestValue(translations) {
309
return `<key>pfm_default</key>
310
<string>${this.enum_[0]}</string>
311
<key>pfm_description</key>
312
<string>${renderProfileString(this.name, this.moduleName, this.description, translations)}</string>
313
<key>pfm_name</key>
314
<string>${this.name}</string>
315
<key>pfm_title</key>
316
<string>${this.name}</string>
317
<key>pfm_type</key>
318
<string>string</string>
319
<key>pfm_range_list</key>
320
<array>
321
${this.enum_.map(e => `<string>${e}</string>`).join('\n ')}
322
</array>`;
323
}
324
}
325
const NumberQ = {
326
Q: `(number) @value`,
327
value(matches) {
328
const match = matches[0];
329
if (!match) {
330
return undefined;
331
}
332
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
333
if (!value) {
334
throw new Error(`Missing required 'value' property.`);
335
}
336
return parseInt(value);
337
}
338
};
339
const StringQ = {
340
Q: `[
341
(string (string_fragment) @value)
342
(call_expression
343
function: [
344
(identifier) @localizeFn (#eq? @localizeFn localize)
345
(member_expression
346
object: (identifier) @nlsObj (#eq? @nlsObj nls)
347
property: (property_identifier) @localizeFn (#eq? @localizeFn localize)
348
)
349
]
350
arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value))
351
)
352
]`,
353
value(matches) {
354
const match = matches[0];
355
if (!match) {
356
return undefined;
357
}
358
const value = match.captures.filter(c => c.name === 'value')[0]?.node.text;
359
if (!value) {
360
throw new Error(`Missing required 'value' property.`);
361
}
362
const nlsKey = match.captures.filter(c => c.name === 'nlsKey')[0]?.node.text;
363
if (nlsKey) {
364
return { value, nlsKey };
365
}
366
else {
367
return value;
368
}
369
}
370
};
371
const StringArrayQ = {
372
Q: `(array ${StringQ.Q})`,
373
value(matches) {
374
if (matches.length === 0) {
375
return undefined;
376
}
377
return matches.map(match => {
378
return StringQ.value([match]);
379
});
380
}
381
};
382
function getProperty(qtype, moduleName, node, key) {
383
const query = new tree_sitter_1.default.Query(typescript, `(
384
(pair
385
key: [(property_identifier)(string)] @key
386
value: ${qtype.Q}
387
)
388
(#any-of? @key "${key}" "'${key}'")
389
)`);
390
try {
391
const matches = query.matches(node).filter(m => m.captures[0].node.parent?.parent === node);
392
return qtype.value(matches);
393
}
394
catch (e) {
395
throw new ParseError(e.message, moduleName, node);
396
}
397
}
398
function getNumberProperty(moduleName, node, key) {
399
return getProperty(NumberQ, moduleName, node, key);
400
}
401
function getStringProperty(moduleName, node, key) {
402
return getProperty(StringQ, moduleName, node, key);
403
}
404
function getStringArrayProperty(moduleName, node, key) {
405
return getProperty(StringArrayQ, moduleName, node, key);
406
}
407
// TODO: add more policy types
408
const PolicyTypes = [
409
BooleanPolicy,
410
NumberPolicy,
411
StringEnumPolicy,
412
StringPolicy,
413
ObjectPolicy
414
];
415
function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) {
416
const name = getStringProperty(moduleName, policyNode, 'name');
417
if (!name) {
418
throw new ParseError(`Missing required 'name' property`, moduleName, policyNode);
419
}
420
else if (isNlsString(name)) {
421
throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode);
422
}
423
const categoryName = getStringProperty(moduleName, configurationNode, 'title');
424
if (!categoryName) {
425
throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode);
426
}
427
else if (!isNlsString(categoryName)) {
428
throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode);
429
}
430
const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`;
431
let category = categories.get(categoryKey);
432
if (!category) {
433
category = { moduleName, name: categoryName };
434
categories.set(categoryKey, category);
435
}
436
const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion');
437
if (!minimumVersion) {
438
throw new ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode);
439
}
440
else if (isNlsString(minimumVersion)) {
441
throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode);
442
}
443
const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description');
444
if (!description) {
445
throw new ParseError(`Missing required 'description' property.`, moduleName, settingNode);
446
}
447
if (!isNlsString(description)) {
448
throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode);
449
}
450
let result;
451
for (const policyType of PolicyTypes) {
452
if (result = policyType.from(name, category, minimumVersion, description, moduleName, settingNode)) {
453
break;
454
}
455
}
456
if (!result) {
457
throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode);
458
}
459
return result;
460
}
461
function getPolicies(moduleName, node) {
462
const query = new tree_sitter_1.default.Query(typescript, `
463
(
464
(call_expression
465
function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration)
466
arguments: (arguments (object (pair
467
key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'")
468
value: (object (pair
469
key: [(property_identifier)(string)(computed_property_name)]
470
value: (object (pair
471
key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'")
472
value: (object) @policy
473
)) @setting
474
))
475
)) @configuration)
476
)
477
)
478
`);
479
const categories = new Map();
480
return query.matches(node).map(m => {
481
const configurationNode = m.captures.filter(c => c.name === 'configuration')[0].node;
482
const settingNode = m.captures.filter(c => c.name === 'setting')[0].node;
483
const policyNode = m.captures.filter(c => c.name === 'policy')[0].node;
484
return getPolicy(moduleName, configurationNode, settingNode, policyNode, categories);
485
});
486
}
487
async function getFiles(root) {
488
return new Promise((c, e) => {
489
const result = [];
490
const rg = (0, child_process_1.spawn)(ripgrep_1.rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]);
491
const stream = (0, byline_1.default)(rg.stdout.setEncoding('utf8'));
492
stream.on('data', path => result.push(path));
493
stream.on('error', err => e(err));
494
stream.on('end', () => c(result));
495
});
496
}
497
function renderADMX(regKey, versions, categories, policies) {
498
versions = versions.map(v => v.replace(/\./g, '_'));
499
return `<?xml version="1.0" encoding="utf-8"?>
500
<policyDefinitions revision="1.1" schemaVersion="1.0">
501
<policyNamespaces>
502
<target prefix="${regKey}" namespace="Microsoft.Policies.${regKey}" />
503
</policyNamespaces>
504
<resources minRequiredRevision="1.0" />
505
<supportedOn>
506
<definitions>
507
${versions.map(v => `<definition name="Supported_${v}" displayName="$(string.Supported_${v})" />`).join(`\n `)}
508
</definitions>
509
</supportedOn>
510
<categories>
511
<category displayName="$(string.Application)" name="Application" />
512
${categories.map(c => `<category displayName="$(string.Category_${c.name.nlsKey})" name="${c.name.nlsKey}"><parentCategory ref="Application" /></category>`).join(`\n `)}
513
</categories>
514
<policies>
515
${policies.map(p => p.renderADMX(regKey)).flat().join(`\n `)}
516
</policies>
517
</policyDefinitions>
518
`;
519
}
520
function renderADML(appName, versions, categories, policies, translations) {
521
return `<?xml version="1.0" encoding="utf-8"?>
522
<policyDefinitionResources revision="1.0" schemaVersion="1.0">
523
<displayName />
524
<description />
525
<resources>
526
<stringTable>
527
<string id="Application">${appName}</string>
528
${versions.map(v => `<string id="Supported_${v.replace(/\./g, '_')}">${appName} &gt;= ${v}</string>`).join(`\n `)}
529
${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations)).join(`\n `)}
530
${policies.map(p => p.renderADMLStrings(translations)).flat().join(`\n `)}
531
</stringTable>
532
<presentationTable>
533
${policies.map(p => p.renderADMLPresentation()).join(`\n `)}
534
</presentationTable>
535
</resources>
536
</policyDefinitionResources>
537
`;
538
}
539
function renderProfileManifest(appName, bundleIdentifier, _versions, _categories, policies, translations) {
540
const requiredPayloadFields = `
541
<dict>
542
<key>pfm_default</key>
543
<string>Configure ${appName}</string>
544
<key>pfm_name</key>
545
<string>PayloadDescription</string>
546
<key>pfm_title</key>
547
<string>Payload Description</string>
548
<key>pfm_type</key>
549
<string>string</string>
550
</dict>
551
<dict>
552
<key>pfm_default</key>
553
<string>${appName}</string>
554
<key>pfm_name</key>
555
<string>PayloadDisplayName</string>
556
<key>pfm_require</key>
557
<string>always</string>
558
<key>pfm_title</key>
559
<string>Payload Display Name</string>
560
<key>pfm_type</key>
561
<string>string</string>
562
</dict>
563
<dict>
564
<key>pfm_default</key>
565
<string>${bundleIdentifier}</string>
566
<key>pfm_name</key>
567
<string>PayloadIdentifier</string>
568
<key>pfm_require</key>
569
<string>always</string>
570
<key>pfm_title</key>
571
<string>Payload Identifier</string>
572
<key>pfm_type</key>
573
<string>string</string>
574
</dict>
575
<dict>
576
<key>pfm_default</key>
577
<string>${bundleIdentifier}</string>
578
<key>pfm_name</key>
579
<string>PayloadType</string>
580
<key>pfm_require</key>
581
<string>always</string>
582
<key>pfm_title</key>
583
<string>Payload Type</string>
584
<key>pfm_type</key>
585
<string>string</string>
586
</dict>
587
<dict>
588
<key>pfm_default</key>
589
<string></string>
590
<key>pfm_name</key>
591
<string>PayloadUUID</string>
592
<key>pfm_require</key>
593
<string>always</string>
594
<key>pfm_title</key>
595
<string>Payload UUID</string>
596
<key>pfm_type</key>
597
<string>string</string>
598
</dict>
599
<dict>
600
<key>pfm_default</key>
601
<integer>1</integer>
602
<key>pfm_name</key>
603
<string>PayloadVersion</string>
604
<key>pfm_range_list</key>
605
<array>
606
<integer>1</integer>
607
</array>
608
<key>pfm_require</key>
609
<string>always</string>
610
<key>pfm_title</key>
611
<string>Payload Version</string>
612
<key>pfm_type</key>
613
<string>integer</string>
614
</dict>
615
<dict>
616
<key>pfm_default</key>
617
<string>Microsoft</string>
618
<key>pfm_name</key>
619
<string>PayloadOrganization</string>
620
<key>pfm_title</key>
621
<string>Payload Organization</string>
622
<key>pfm_type</key>
623
<string>string</string>
624
</dict>`;
625
const profileManifestSubkeys = policies.map(policy => {
626
return policy.renderProfileManifest(translations);
627
}).join('');
628
return `<?xml version="1.0" encoding="UTF-8"?>
629
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
630
<plist version="1.0">
631
<dict>
632
<key>pfm_app_url</key>
633
<string>https://code.visualstudio.com/</string>
634
<key>pfm_description</key>
635
<string>${appName} Managed Settings</string>
636
<key>pfm_documentation_url</key>
637
<string>https://code.visualstudio.com/docs/setup/enterprise</string>
638
<key>pfm_domain</key>
639
<string>${bundleIdentifier}</string>
640
<key>pfm_format_version</key>
641
<integer>1</integer>
642
<key>pfm_interaction</key>
643
<string>combined</string>
644
<key>pfm_last_modified</key>
645
<date>${new Date().toISOString().replace(/\.\d+Z$/, 'Z')}</date>
646
<key>pfm_platforms</key>
647
<array>
648
<string>macOS</string>
649
</array>
650
<key>pfm_subkeys</key>
651
<array>
652
${requiredPayloadFields}
653
${profileManifestSubkeys}
654
</array>
655
<key>pfm_title</key>
656
<string>${appName}</string>
657
<key>pfm_unique</key>
658
<true/>
659
<key>pfm_version</key>
660
<integer>1</integer>
661
</dict>
662
</plist>`;
663
}
664
function renderMacOSPolicy(policies, translations) {
665
const appName = product.nameLong;
666
const bundleIdentifier = product.darwinBundleIdentifier;
667
const payloadUUID = product.darwinProfilePayloadUUID;
668
const UUID = product.darwinProfileUUID;
669
const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort();
670
const categories = [...new Set(policies.map(p => p.category))];
671
const policyEntries = policies.map(policy => policy.renderProfile())
672
.flat()
673
.map(entry => `\t\t\t\t${entry}`)
674
.join('\n');
675
return {
676
profile: `<?xml version="1.0" encoding="UTF-8"?>
677
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
678
<plist version="1.0">
679
<dict>
680
<key>PayloadContent</key>
681
<array>
682
<dict>
683
<key>PayloadDisplayName</key>
684
<string>${appName}</string>
685
<key>PayloadIdentifier</key>
686
<string>${bundleIdentifier}.${UUID}</string>
687
<key>PayloadType</key>
688
<string>${bundleIdentifier}</string>
689
<key>PayloadUUID</key>
690
<string>${UUID}</string>
691
<key>PayloadVersion</key>
692
<integer>1</integer>
693
${policyEntries}
694
</dict>
695
</array>
696
<key>PayloadDescription</key>
697
<string>This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise</string>
698
<key>PayloadDisplayName</key>
699
<string>${appName}</string>
700
<key>PayloadIdentifier</key>
701
<string>${bundleIdentifier}</string>
702
<key>PayloadOrganization</key>
703
<string>Microsoft</string>
704
<key>PayloadType</key>
705
<string>Configuration</string>
706
<key>PayloadUUID</key>
707
<string>${payloadUUID}</string>
708
<key>PayloadVersion</key>
709
<integer>1</integer>
710
<key>TargetDeviceType</key>
711
<integer>5</integer>
712
</dict>
713
</plist>`,
714
manifests: [{ languageId: 'en-us', contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies) },
715
...translations.map(({ languageId, languageTranslations }) => ({ languageId, contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies, languageTranslations) }))
716
]
717
};
718
}
719
function renderGP(policies, translations) {
720
const appName = product.nameLong;
721
const regKey = product.win32RegValueName;
722
const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort();
723
const categories = [...Object.values(policies.reduce((acc, p) => ({ ...acc, [p.category.name.nlsKey]: p.category }), {}))];
724
return {
725
admx: renderADMX(regKey, versions, categories, policies),
726
adml: [
727
{ languageId: 'en-us', contents: renderADML(appName, versions, categories, policies) },
728
...translations.map(({ languageId, languageTranslations }) => ({ languageId, contents: renderADML(appName, versions, categories, policies, languageTranslations) }))
729
]
730
};
731
}
732
const Languages = {
733
'fr': 'fr-fr',
734
'it': 'it-it',
735
'de': 'de-de',
736
'es': 'es-es',
737
'ru': 'ru-ru',
738
'zh-hans': 'zh-cn',
739
'zh-hant': 'zh-tw',
740
'ja': 'ja-jp',
741
'ko': 'ko-kr',
742
'cs': 'cs-cz',
743
'pt-br': 'pt-br',
744
'tr': 'tr-tr',
745
'pl': 'pl-pl',
746
};
747
async function getSpecificNLS(resourceUrlTemplate, languageId, version) {
748
const resource = {
749
publisher: 'ms-ceintl',
750
name: `vscode-language-pack-${languageId}`,
751
version: `${version[0]}.${version[1]}.${version[2]}`,
752
path: 'extension/translations/main.i18n.json'
753
};
754
const url = resourceUrlTemplate.replace(/\{([^}]+)\}/g, (_, key) => resource[key]);
755
const res = await fetch(url);
756
if (res.status !== 200) {
757
throw new Error(`[${res.status}] Error downloading language pack ${languageId}@${version}`);
758
}
759
const { contents: result } = await res.json();
760
return result;
761
}
762
function parseVersion(version) {
763
const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
764
return [parseInt(major), parseInt(minor), parseInt(patch)];
765
}
766
function compareVersions(a, b) {
767
if (a[0] !== b[0]) {
768
return a[0] - b[0];
769
}
770
if (a[1] !== b[1]) {
771
return a[1] - b[1];
772
}
773
return a[2] - b[2];
774
}
775
async function queryVersions(serviceUrl, languageId) {
776
const res = await fetch(`${serviceUrl}/extensionquery`, {
777
method: 'POST',
778
headers: {
779
'Accept': 'application/json;api-version=3.0-preview.1',
780
'Content-Type': 'application/json',
781
'User-Agent': 'VS Code Build',
782
},
783
body: JSON.stringify({
784
filters: [{ criteria: [{ filterType: 7, value: `ms-ceintl.vscode-language-pack-${languageId}` }] }],
785
flags: 0x1
786
})
787
});
788
if (res.status !== 200) {
789
throw new Error(`[${res.status}] Error querying for extension: ${languageId}`);
790
}
791
const result = await res.json();
792
return result.results[0].extensions[0].versions.map(v => parseVersion(v.version)).sort(compareVersions);
793
}
794
async function getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) {
795
const versions = await queryVersions(extensionGalleryServiceUrl, languageId);
796
const nextMinor = [version[0], version[1] + 1, 0];
797
const compatibleVersions = versions.filter(v => compareVersions(v, nextMinor) < 0);
798
const latestCompatibleVersion = compatibleVersions.at(-1); // order is newest to oldest
799
if (!latestCompatibleVersion) {
800
throw new Error(`No compatible language pack found for ${languageId} for version ${version}`);
801
}
802
return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion);
803
}
804
async function parsePolicies() {
805
const parser = new tree_sitter_1.default();
806
parser.setLanguage(typescript);
807
const files = await getFiles(process.cwd());
808
const base = path_1.default.join(process.cwd(), 'src');
809
const policies = [];
810
for (const file of files) {
811
const moduleName = path_1.default.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/');
812
const contents = await fs_1.promises.readFile(file, { encoding: 'utf8' });
813
const tree = parser.parse(contents);
814
policies.push(...getPolicies(moduleName, tree.rootNode));
815
}
816
return policies;
817
}
818
async function getTranslations() {
819
const extensionGalleryServiceUrl = product.extensionsGallery?.serviceUrl;
820
if (!extensionGalleryServiceUrl) {
821
console.warn(`Skipping policy localization: No 'extensionGallery.serviceUrl' found in 'product.json'.`);
822
return [];
823
}
824
const resourceUrlTemplate = product.extensionsGallery?.resourceUrlTemplate;
825
if (!resourceUrlTemplate) {
826
console.warn(`Skipping policy localization: No 'resourceUrlTemplate' found in 'product.json'.`);
827
return [];
828
}
829
const version = parseVersion(packageJson.version);
830
const languageIds = Object.keys(Languages);
831
return await Promise.all(languageIds.map(languageId => getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version)
832
.then(languageTranslations => ({ languageId, languageTranslations }))));
833
}
834
async function windowsMain(policies, translations) {
835
const root = '.build/policies/win32';
836
const { admx, adml } = await renderGP(policies, translations);
837
await fs_1.promises.rm(root, { recursive: true, force: true });
838
await fs_1.promises.mkdir(root, { recursive: true });
839
await fs_1.promises.writeFile(path_1.default.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n'));
840
for (const { languageId, contents } of adml) {
841
const languagePath = path_1.default.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]);
842
await fs_1.promises.mkdir(languagePath, { recursive: true });
843
await fs_1.promises.writeFile(path_1.default.join(languagePath, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n'));
844
}
845
}
846
async function darwinMain(policies, translations) {
847
const bundleIdentifier = product.darwinBundleIdentifier;
848
if (!bundleIdentifier || !product.darwinProfilePayloadUUID || !product.darwinProfileUUID) {
849
throw new Error(`Missing required product information.`);
850
}
851
const root = '.build/policies/darwin';
852
const { profile, manifests } = await renderMacOSPolicy(policies, translations);
853
await fs_1.promises.rm(root, { recursive: true, force: true });
854
await fs_1.promises.mkdir(root, { recursive: true });
855
await fs_1.promises.writeFile(path_1.default.join(root, `${bundleIdentifier}.mobileconfig`), profile.replace(/\r?\n/g, '\n'));
856
for (const { languageId, contents } of manifests) {
857
const languagePath = path_1.default.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]);
858
await fs_1.promises.mkdir(languagePath, { recursive: true });
859
await fs_1.promises.writeFile(path_1.default.join(languagePath, `${bundleIdentifier}.plist`), contents.replace(/\r?\n/g, '\n'));
860
}
861
}
862
async function main() {
863
const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]);
864
const platform = process.argv[2];
865
if (platform === 'darwin') {
866
await darwinMain(policies, translations);
867
}
868
else if (platform === 'win32') {
869
await windowsMain(policies, translations);
870
}
871
else {
872
console.error(`Usage: node build/lib/policies <darwin|win32>`);
873
process.exit(1);
874
}
875
}
876
if (require.main === module) {
877
main().catch(err => {
878
if (err instanceof ParseError) {
879
console.error(`Parse Error:`, err.message);
880
}
881
else {
882
console.error(err);
883
}
884
process.exit(1);
885
});
886
}
887
//# sourceMappingURL=policies.js.map
888