vscode-nls
Advanced tools
Comparing version 3.0.0 to 3.1.0-next.1
@@ -0,4 +1,10 @@ | ||
export declare enum MessageFormat { | ||
file = "file", | ||
bundle = "bundle", | ||
both = "both", | ||
} | ||
export interface Options { | ||
locale?: string; | ||
cacheLanguageResolution?: boolean; | ||
messageFormat?: MessageFormat; | ||
} | ||
@@ -16,14 +22,4 @@ export interface LocalizeInfo { | ||
} | ||
export declare enum ExtensionKind { | ||
prePackaged = 1, | ||
marketPlace = 2, | ||
} | ||
export declare type KeyInfo = string | LocalizeInfo; | ||
export declare function loadMessageBundle(file?: string): LocalizeFunc; | ||
export declare function config(): LoadFunc; | ||
export declare function config(opt: Options): LoadFunc; | ||
export declare function config(opt: string): LoadFunc; | ||
export declare function config(opt: Options, root: string, outDir: string): LoadFunc; | ||
export declare function config(opt: string, root: string, outDir: string): LoadFunc; | ||
export declare function config(opt: string, kind: ExtensionKind.prePackaged, root: string, outDir?: string): LoadFunc; | ||
export declare function config(opt: string, kind: ExtensionKind.marketPlace, root: string, outDir: string): LoadFunc; | ||
export declare function config(opts?: Options): LoadFunc; |
482
lib/main.js
@@ -9,38 +9,2 @@ /* -------------------------------------------------------------------------------------------- | ||
var fs = require("fs"); | ||
var LocalizeInfo; | ||
(function (LocalizeInfo) { | ||
function is(value) { | ||
var candidate = value; | ||
return candidate && isDefined(candidate.key) && isDefined(candidate.comment); | ||
} | ||
LocalizeInfo.is = is; | ||
})(LocalizeInfo || (LocalizeInfo = {})); | ||
var ExtensionKind; | ||
(function (ExtensionKind) { | ||
ExtensionKind[ExtensionKind["prePackaged"] = 1] = "prePackaged"; | ||
ExtensionKind[ExtensionKind["marketPlace"] = 2] = "marketPlace"; | ||
})(ExtensionKind = exports.ExtensionKind || (exports.ExtensionKind = {})); | ||
var _options; | ||
var _isPseudo; | ||
var _extensionKind; | ||
var _root; | ||
var _outDir; | ||
var _outPath; | ||
var _resolvedLanguage; | ||
// If undefined we never tried to load. If null we tried to load (the bundle exists on disk) | ||
// but the actual load failed. | ||
var _resolvedBundle; | ||
var _resolvedCacheLocation; | ||
function initializeSettings() { | ||
_options = { locale: undefined, cacheLanguageResolution: true }; | ||
_isPseudo = false; | ||
_extensionKind = undefined; | ||
_root = undefined; | ||
_outDir = undefined; | ||
_outPath = undefined; | ||
_resolvedLanguage = undefined; | ||
_resolvedBundle = undefined; | ||
_resolvedCacheLocation = undefined; | ||
} | ||
initializeSettings(); | ||
var toString = Object.prototype.toString; | ||
@@ -59,5 +23,53 @@ function isDefined(value) { | ||
} | ||
var MessageFormat; | ||
(function (MessageFormat) { | ||
MessageFormat["file"] = "file"; | ||
MessageFormat["bundle"] = "bundle"; | ||
MessageFormat["both"] = "both"; | ||
})(MessageFormat = exports.MessageFormat || (exports.MessageFormat = {})); | ||
var LocalizeInfo; | ||
(function (LocalizeInfo) { | ||
function is(value) { | ||
var candidate = value; | ||
return candidate && isDefined(candidate.key) && isDefined(candidate.comment); | ||
} | ||
LocalizeInfo.is = is; | ||
})(LocalizeInfo || (LocalizeInfo = {})); | ||
var resolvedLanguage; | ||
var resolvedBundles; | ||
var options; | ||
var isPseudo; | ||
function initializeSettings() { | ||
options = { locale: undefined, cacheLanguageResolution: true, messageFormat: MessageFormat.bundle }; | ||
if (isString(process.env.VSCODE_NLS_CONFIG)) { | ||
try { | ||
var vscodeOptions = JSON.parse(process.env.VSCODE_NLS_CONFIG); | ||
if (isString(vscodeOptions.locale)) { | ||
options.locale = vscodeOptions.locale.toLowerCase(); | ||
} | ||
if (isString(vscodeOptions._cacheRoot)) { | ||
options.cacheRoot = vscodeOptions._cacheRoot; | ||
} | ||
if (isString(vscodeOptions._languagePackId)) { | ||
options.languagePackId = vscodeOptions._languagePackId; | ||
} | ||
if (isString(vscodeOptions._languagePackLocation)) { | ||
options.languagePackLocation = vscodeOptions._languagePackLocation; | ||
} | ||
} | ||
catch (_a) { | ||
// Do nothing. | ||
} | ||
} | ||
isPseudo = options.locale === 'pseudo'; | ||
resolvedLanguage = undefined; | ||
resolvedBundles = Object.create(null); | ||
} | ||
initializeSettings(); | ||
function supportsLanguagePack() { | ||
return options.cacheRoot !== undefined && options.languagePackId !== undefined && options.languagePackLocation !== undefined; | ||
} | ||
function format(message, args) { | ||
var result; | ||
if (_isPseudo) { | ||
if (isPseudo) { | ||
// FF3B and FF3D is the Unicode zenkaku representation for [ and ] | ||
@@ -108,39 +120,13 @@ message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; | ||
} | ||
function tryResolveBundle(root) { | ||
var locale = _options.locale; | ||
while (locale) { | ||
var candidate = path.join(root, "nls.bundle." + locale + ".json"); | ||
if (fs.existsSync(candidate)) { | ||
return candidate; | ||
} | ||
else { | ||
var index = locale.lastIndexOf('-'); | ||
if (index > 0) { | ||
locale = locale.substring(0, index); | ||
} | ||
else { | ||
locale = undefined; | ||
} | ||
} | ||
} | ||
// Test if we can reslove the default bundle. | ||
if (locale === undefined) { | ||
var candidate = path.join(root, 'nls.bundle.json'); | ||
if (fs.existsSync(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
return undefined; | ||
} | ||
function resolveLanguage(file) { | ||
var resolvedLanguage; | ||
if (_options.cacheLanguageResolution && _resolvedLanguage) { | ||
resolvedLanguage = _resolvedLanguage; | ||
if (options.cacheLanguageResolution && resolvedLanguage) { | ||
resolvedLanguage = resolvedLanguage; | ||
} | ||
else { | ||
if (_isPseudo || !_options.locale) { | ||
if (isPseudo || !options.locale) { | ||
resolvedLanguage = '.nls.json'; | ||
} | ||
else { | ||
var locale = _options.locale; | ||
var locale = options.locale; | ||
while (locale) { | ||
@@ -164,4 +150,4 @@ var candidate = '.nls.' + locale + '.json'; | ||
} | ||
if (_options.cacheLanguageResolution) { | ||
_resolvedLanguage = resolvedLanguage; | ||
if (options.cacheLanguageResolution) { | ||
resolvedLanguage = resolvedLanguage; | ||
} | ||
@@ -171,2 +157,28 @@ } | ||
} | ||
function findInTheBoxBundle(root) { | ||
var locale = options.locale; | ||
while (locale) { | ||
var candidate = path.join(root, "nls.bundle." + locale + ".json"); | ||
if (fs.existsSync(candidate)) { | ||
return candidate; | ||
} | ||
else { | ||
var index = locale.lastIndexOf('-'); | ||
if (index > 0) { | ||
locale = locale.substring(0, index); | ||
} | ||
else { | ||
locale = undefined; | ||
} | ||
} | ||
} | ||
// Test if we can reslove the default bundle. | ||
if (locale === undefined) { | ||
var candidate = path.join(root, 'nls.bundle.json'); | ||
if (fs.existsSync(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
return undefined; | ||
} | ||
function mkdir(directory) { | ||
@@ -192,15 +204,7 @@ try { | ||
} | ||
function supportsLanguagePack() { | ||
if (!_root || !_outDir || !_options._languagePackLocation | ||
|| (_extensionKind === ExtensionKind.prePackaged && !_options._resolvedLanguagePackExtensionLocation) | ||
|| (_extensionKind === ExtensionKind.marketPlace && !_options._cacheRoot)) { | ||
return false; | ||
} | ||
return fs.existsSync(path.join(_outPath, 'nls.metadata.json')); | ||
} | ||
function loadDefaultBundle() { | ||
var metaData = require(path.join(_outPath, 'nls.metadata.json')); | ||
function createDefaultNlsBundle(folder) { | ||
var metaData = require(path.join(folder, 'nls.metadata.json')); | ||
var result = Object.create(null); | ||
for (var module_1 in metaData.content) { | ||
var entry = metaData.content[module_1]; | ||
for (var module_1 in metaData) { | ||
var entry = metaData[module_1]; | ||
result[module_1] = entry.messages; | ||
@@ -210,9 +214,9 @@ } | ||
} | ||
function createLanguageBundle(extensionName) { | ||
var languagePack = require(path.join(_options._languagePackLocation, 'extensions', extensionName + ".i18n.json")).contents; | ||
var metaData = require(path.join(_outPath, 'nls.metadata.json')); | ||
function createNLSBundle(header, metaDataPath) { | ||
var languagePack = require(path.join(options.languagePackLocation, 'extensions', header.id + ".i18n.json")).contents; | ||
var metaData = require(path.join(metaDataPath, 'nls.metadata.json')); | ||
var result = Object.create(null); | ||
for (var module_2 in metaData.content) { | ||
var entry = metaData.content[module_2]; | ||
var translations = languagePack[_outDir + "/" + module_2]; | ||
for (var module_2 in metaData) { | ||
var entry = metaData[module_2]; | ||
var translations = languagePack[header.outDir + "/" + module_2]; | ||
if (translations) { | ||
@@ -237,14 +241,26 @@ var resultMessages = []; | ||
} | ||
function loadPrepackagedLanguagePackBundle() { | ||
var extensionName = require(path.join(_root, 'package.json')).name; | ||
var root = path.join(_options._resolvedLanguagePackExtensionLocation); | ||
var bundle = path.join(root, extensionName + ".nls.json"); | ||
function touch(file) { | ||
var d = new Date(); | ||
fs.utimes(file, d, d, function () { | ||
// Do nothing. Ignore | ||
}); | ||
} | ||
function cacheBundle(key, bundle) { | ||
resolvedBundles[key] = bundle; | ||
return bundle; | ||
} | ||
function loadNlsBundleOrCreateFromI18n(header, bundlePath) { | ||
var result; | ||
var root = path.join(options.cacheRoot, "" + header.id); | ||
var bundle = path.join(root, header.hash + "-" + options.languagePackId + ".nls.json"); | ||
var useMemoryOnly = false; | ||
var noEntry = false; | ||
var writeBundle = false; | ||
try { | ||
return JSON.parse(fs.readFileSync(bundle, { encoding: 'utf8', flag: 'r' })); | ||
result = JSON.parse(fs.readFileSync(bundle, { encoding: 'utf8', flag: 'r' })); | ||
touch(bundle); | ||
return result; | ||
} | ||
catch (err) { | ||
if (err.code === 'ENOENT') { | ||
noEntry = true; | ||
writeBundle = true; | ||
} | ||
@@ -260,7 +276,7 @@ else if (err instanceof SyntaxError) { | ||
} | ||
var result = createLanguageBundle(extensionName); | ||
result = createNLSBundle(header, bundlePath); | ||
if (useMemoryOnly) { | ||
return result; | ||
} | ||
if (noEntry) { | ||
if (writeBundle) { | ||
mkdir(root); | ||
@@ -279,57 +295,44 @@ try { | ||
} | ||
function loadMarketPlaceLanguagePackBundle() { | ||
var packageJson = require(path.join(_root, 'package.json')); | ||
var extensionName = packageJson.name; | ||
var extensionVersion = packageJson.version; | ||
var extensionPublisher = packageJson.publisher; | ||
if (!extensionName || !extensionVersion || !extensionPublisher) { | ||
return undefined; | ||
function loadNlsBundle(header, bundlePath) { | ||
var result; | ||
// Core decided to use a language pack. Do the same in the extension | ||
if (options.languagePackId) { | ||
result = loadNlsBundleOrCreateFromI18n(header, bundlePath); | ||
} | ||
var root = path.join(_options._cacheRoot, extensionName + "." + extensionPublisher + "-" + extensionVersion); | ||
var useMemoryOnly = false; | ||
var noEntry = false; | ||
// We already have a cached bundle. Use it. | ||
var bundle = tryResolveBundle(root); | ||
var result; | ||
if (bundle) { | ||
try { | ||
result = JSON.parse(fs.readFileSync(bundle, { encoding: 'utf8', flag: 'r' })); | ||
if (!result) { | ||
var candidate = findInTheBoxBundle(bundlePath); | ||
if (candidate) { | ||
try { | ||
// Touch last used. | ||
fs.writeFileSync(path.join(root, 'lastUsed.touch'), '', { encoding: 'utf8', flag: 'w' }); | ||
return require(candidate); | ||
} | ||
catch (err) { | ||
// Ignore error. | ||
console.error("Loading in the box message bundle failed.", err); | ||
} | ||
return result; | ||
} | ||
try { | ||
result = createDefaultNlsBundle(bundlePath); | ||
} | ||
catch (err) { | ||
if (err.code === 'ENOENT') { | ||
noEntry = true; | ||
} | ||
else if (err instanceof SyntaxError) { | ||
// We have a syntax error. So no valid JSON. Use | ||
console.error("Syntax error parsing message bundle.", err); | ||
useMemoryOnly = true; | ||
} | ||
else { | ||
throw err; | ||
} | ||
console.error("Generating default bundle from meta data failed.", err); | ||
result = undefined; | ||
} | ||
} | ||
result = createLanguageBundle(extensionName); | ||
if (useMemoryOnly) { | ||
return result; | ||
} | ||
if (noEntry) { | ||
mkdir(root); | ||
try { | ||
fs.writeFileSync(bundle, JSON.stringify(result), { encoding: 'utf8', flag: 'wx' }); | ||
return result; | ||
} | ||
function tryFindMetaDataHeaderFile(file) { | ||
var result; | ||
var dirname = path.dirname(file); | ||
while (true) { | ||
result = path.join(dirname, 'nls.metadata.header.json'); | ||
if (fs.existsSync(result)) { | ||
break; | ||
} | ||
catch (err) { | ||
if (err.code === 'EEXIST') { | ||
return result; | ||
} | ||
throw err; | ||
var parent = path.dirname(dirname); | ||
if (parent === dirname) { | ||
result = undefined; | ||
break; | ||
} | ||
else { | ||
dirname = parent; | ||
} | ||
} | ||
@@ -349,158 +352,75 @@ return result; | ||
} | ||
// We have a single resolved bundle. | ||
if (_resolvedBundle !== undefined) { | ||
// We failed resolve the bundle including generating one from meta data. | ||
// We return a special localize function that always errors | ||
if (_resolvedBundle === null) { | ||
return function () { | ||
return 'Failed to load message bundle. See console for details.'; | ||
}; | ||
} | ||
if (!file.startsWith(_outPath)) { | ||
console.error("Mismatch between out path(" + _outPath + ") and file location(" + file + ")"); | ||
return function () { | ||
return 'Location mismatch. See console for details.'; | ||
}; | ||
} | ||
var module_3 = file.substr(_outPath.length + 1).replace(/\\/g, '/'); | ||
var messages = _resolvedBundle[module_3]; | ||
if (messages === undefined) { | ||
console.error("Messages for file " + file + " not found. See console for details."); | ||
return function () { | ||
return 'Messages not found'; | ||
}; | ||
} | ||
return createScopedLocalizeFunction(messages); | ||
} | ||
// Try to load a single file bundle | ||
try { | ||
var json = require(resolveLanguage(file)); | ||
if (Array.isArray(json)) { | ||
return createScopedLocalizeFunction(json); | ||
} | ||
else { | ||
if (isDefined(json.messages) && isDefined(json.keys)) { | ||
return createScopedLocalizeFunction(json.messages); | ||
if (options.messageFormat === MessageFormat.both || options.messageFormat === MessageFormat.bundle) { | ||
var headerFile = tryFindMetaDataHeaderFile(file); | ||
if (headerFile) { | ||
var bundlePath = path.dirname(headerFile); | ||
var bundle = resolvedBundles[bundlePath]; | ||
if (bundle === undefined) { | ||
try { | ||
var header = JSON.parse(fs.readFileSync(headerFile, 'utf8')); | ||
var nlsBundle = loadNlsBundle(header, bundlePath); | ||
bundle = cacheBundle(bundlePath, nlsBundle ? { header: header, nlsBundle: nlsBundle } : null); | ||
} | ||
catch (err) { | ||
console.error('Failed to read header file', err); | ||
bundle = cacheBundle(bundlePath, null); | ||
} | ||
} | ||
else { | ||
console.error("String bundle '" + file + "' uses an unsupported format."); | ||
return function () { | ||
return 'File bundle has unsupported format. See console for details'; | ||
}; | ||
if (bundle) { | ||
var outPath = path.join(bundlePath, bundle.header.outDir); | ||
var module_3 = file.substr(outPath.length + 1).replace(/\\/g, '/'); | ||
var messages = bundle.nlsBundle[module_3]; | ||
if (messages === undefined) { | ||
console.error("Messages for file " + file + " not found. See console for details."); | ||
return function () { | ||
return 'Messages not found.'; | ||
}; | ||
} | ||
return createScopedLocalizeFunction(messages); | ||
} | ||
} | ||
} | ||
catch (err) { | ||
console.error("Can't load string bundle for " + file, err); | ||
return function () { | ||
return 'Failed to load file bundle. See console for details.'; | ||
}; | ||
} | ||
} | ||
exports.loadMessageBundle = loadMessageBundle; | ||
function config(opt, rootOrKind, outDirOrRoot, outDir) { | ||
initializeSettings(); | ||
var options; | ||
if (isString(opt)) { | ||
if (options.messageFormat === MessageFormat.both || options.messageFormat === MessageFormat.file) { | ||
// Try to load a single file bundle | ||
try { | ||
options = JSON.parse(opt); | ||
} | ||
catch (e) { | ||
console.error("Error parsing nls options: " + opt); | ||
} | ||
} | ||
else { | ||
options = opt; | ||
} | ||
var kind; | ||
var root; | ||
if (isString(rootOrKind)) { | ||
kind = undefined; | ||
root = rootOrKind; | ||
outDir = outDirOrRoot; | ||
} | ||
else { | ||
kind = rootOrKind; | ||
root = outDirOrRoot; | ||
} | ||
if (options) { | ||
if (isString(options.locale)) { | ||
_options.locale = options.locale.toLowerCase(); | ||
_resolvedLanguage = undefined; | ||
_resolvedCacheLocation = undefined; | ||
} | ||
var asInternal = options; | ||
if (isString(asInternal._cacheRoot)) { | ||
_options._cacheRoot = asInternal._cacheRoot; | ||
} | ||
if (isString(asInternal._languagePackLocation)) { | ||
_options._languagePackLocation = asInternal._languagePackLocation; | ||
} | ||
if (isString(asInternal._resolvedLanguagePackCoreLocation)) { | ||
_options._resolvedLanguagePackCoreLocation = asInternal._resolvedLanguagePackCoreLocation; | ||
} | ||
if (isString(asInternal._resolvedLanguagePackExtensionLocation)) { | ||
_options._resolvedLanguagePackExtensionLocation = asInternal._resolvedLanguagePackExtensionLocation; | ||
} | ||
if (isBoolean(options.cacheLanguageResolution)) { | ||
_options.cacheLanguageResolution = options.cacheLanguageResolution; | ||
} | ||
} | ||
var packageJsonExists = false; | ||
if (kind === ExtensionKind.prePackaged && isString(root) && root.length > 0 && !outDir) { | ||
var parent = path.dirname(root); | ||
var basename = path.basename(root); | ||
if (fs.existsSync(path.join(parent, 'package.json'))) { | ||
packageJsonExists = true; | ||
root = parent; | ||
outDir = basename; | ||
} | ||
} | ||
if (root && path.isAbsolute(root)) { | ||
_root = root; | ||
_outDir = outDir; | ||
if (_outDir) { | ||
_outPath = path.join(_root, _outDir); | ||
} | ||
} | ||
if (kind && _root && _outDir && (packageJsonExists || fs.existsSync(path.join(root, 'package.json')))) { | ||
_extensionKind = kind; | ||
} | ||
_isPseudo = _options.locale === 'pseudo'; | ||
// We have a root and a outDir. So we can try to load a bundle if it exists. | ||
if (_root && _outDir) { | ||
// We are using a language pack. | ||
if (supportsLanguagePack()) { | ||
try { | ||
if (kind === ExtensionKind.prePackaged) { | ||
_resolvedBundle = loadPrepackagedLanguagePackBundle(); | ||
var json = require(resolveLanguage(file)); | ||
if (Array.isArray(json)) { | ||
return createScopedLocalizeFunction(json); | ||
} | ||
else { | ||
if (isDefined(json.messages) && isDefined(json.keys)) { | ||
return createScopedLocalizeFunction(json.messages); | ||
} | ||
else { | ||
_resolvedBundle = loadMarketPlaceLanguagePackBundle(); | ||
console.error("String bundle '" + file + "' uses an unsupported format."); | ||
return function () { | ||
return 'File bundle has unsupported format. See console for details'; | ||
}; | ||
} | ||
return loadMessageBundle; | ||
} | ||
catch (err) { | ||
console.error("Loading the message bundle from language pack failed with exception", err); | ||
} | ||
} | ||
var candidate = tryResolveBundle(path.join(_root, _outDir)); | ||
if (candidate) { | ||
try { | ||
_resolvedBundle = require(candidate); | ||
return loadMessageBundle; | ||
catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
console.error('Failed to load single file bundle', err); | ||
} | ||
catch (err) { | ||
console.error("Loading in the box message bundle failed.", err); | ||
} | ||
} | ||
try { | ||
_resolvedBundle = loadDefaultBundle(); | ||
return loadMessageBundle; | ||
} | ||
console.error("Failed to load message bundle for file " + file); | ||
return function () { | ||
return 'Failed to load message bundle. See console for details.'; | ||
}; | ||
} | ||
exports.loadMessageBundle = loadMessageBundle; | ||
function config(opts) { | ||
if (opts) { | ||
if (isString(opts.locale)) { | ||
options.locale = opts.locale.toLowerCase(); | ||
resolvedLanguage = undefined; | ||
resolvedBundles = Object.create(null); | ||
} | ||
catch (err) { | ||
console.error("Generating default bundle from meta data failed.", err); | ||
_resolvedBundle = null; | ||
if (opts.messageFormat !== undefined) { | ||
options.messageFormat = opts.messageFormat; | ||
} | ||
} | ||
isPseudo = options.locale === 'pseudo'; | ||
return loadMessageBundle; | ||
@@ -507,0 +427,0 @@ } |
{ | ||
"name": "vscode-nls", | ||
"version": "3.0.0", | ||
"version": "3.1.0-next.1", | ||
"description": "NPM module to externalize and localize VSCode extensions", | ||
@@ -5,0 +5,0 @@ "author": "Microsoft Corporation", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
21349
441
2