Skip to content

Commit aeff9ba

Browse files
shubhekshakumar303
authored andcommitted
feat: web-ext build now uses the default locale (if applicable) when generating a file name (#487)
* fix: Building an XPI with only localizations makes an ugly file name * fix: Building an XPI with only localizations makes an ugly file name * fix: Building an XPI with only localizations makes an ugly file name -- works with extensions which aren't localized * fix: Building an XPI with only localizations makes an ugly file name -- resolved merge conflicts * test: Building an XPI with only localizations makes an ugly file name * test: removed unnecessary locales * refactor: isolated the method to retrive the name of the extension * fix: Building an XPI with only localizations makes an ugly file name * test: Building an XPI with only localizations makes an ugly file name--added test for malformed JSON * test: Building an XPI with only localizations makes an ugly file name--added test for malformed JSON * refactor: Building an XPI with only localizations makes an ugly file name * refactor: getDefaultLocalizedName() accepts a file instead of a JSON string -- modified the test checking for malformed JSON accordingly * test: name of extension which is not localized * test: check if locale file does not exist * test: check locale file for incorrect format * fix: style nits and removed unnecessary test * test: fixed error messages for malformed json and incorrect locale file format tests * test: check gives correct name to a localized extension with repeating pattern in manifest * test: made error messages clearer, fixed style nits * test: cleaned up code, fixed style nits, made error message clearer
1 parent d0954a9 commit aeff9ba

File tree

6 files changed

+183
-4
lines changed

6 files changed

+183
-4
lines changed

src/cmd/build.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import path from 'path';
33
import minimatch from 'minimatch';
44
import {createWriteStream} from 'fs';
55
import streamToPromise from 'stream-to-promise';
6+
import {fs} from 'mz';
67

78
import defaultSourceWatcher from '../watcher';
89
import {zipDir} from '../util/zip-dir';
910
import getValidatedManifest, {getManifestId} from '../util/manifest';
1011
import {prepareArtifactsDir} from '../util/artifacts';
1112
import {createLogger} from '../util/logger';
13+
import {UsageError} from '../errors';
1214

1315

1416
const log = createLogger(__filename);
@@ -37,14 +39,48 @@ export type PackageCreatorParams = {
3739
artifactsDir: string,
3840
};
3941

42+
export type LocalizedNameParams = {
43+
messageFile: string,
44+
manifestData: ExtensionManifest,
45+
}
46+
47+
export async function getDefaultLocalizedName(
48+
{messageFile, manifestData}: LocalizedNameParams
49+
): Promise<string> {
50+
51+
let messageData: string|Buffer;
52+
let extensionName: string = manifestData.name;
53+
54+
try {
55+
messageData = JSON.parse(await fs.readFile(messageFile));
56+
}
57+
catch (error) {
58+
throw new UsageError(
59+
`Error reading or parsing file ${messageFile}: ${error}`);
60+
}
61+
extensionName = manifestData.name.replace(/__MSG_([A-Za-z0-9@_]+?)__/g,
62+
(match, messageName) => {
63+
if (!(messageData[messageName]
64+
&& messageData[messageName].message)) {
65+
const error = new UsageError(
66+
`The locale file ${messageFile} ` +
67+
`is missing key: ${messageName}`);
68+
throw error;
69+
}
70+
else {
71+
return messageData[messageName].message;
72+
}
73+
});
74+
return Promise.resolve(extensionName);
75+
}
76+
4077
export type PackageCreatorFn =
4178
(params: PackageCreatorParams) => Promise<ExtensionBuildResult>;
4279

4380
async function defaultPackageCreator(
4481
{manifestData, sourceDir, fileFilter, artifactsDir}: PackageCreatorParams
4582
): Promise<ExtensionBuildResult> {
4683
let id;
47-
4884
if (manifestData) {
4985
id = getManifestId(manifestData);
5086
log.debug(`Using manifest id=${id || '[not specified]'}`);
@@ -56,8 +92,17 @@ async function defaultPackageCreator(
5692
filter: (...args) => fileFilter.wantFile(...args),
5793
});
5894

95+
let extensionName: string = manifestData.name;
96+
97+
if (manifestData.default_locale) {
98+
let messageFile = path.join(sourceDir, '_locales',
99+
manifestData.default_locale, 'messages.json');
100+
log.debug('Manifest declared default_locale, localizing extension name');
101+
extensionName = await getDefaultLocalizedName(
102+
{messageFile, manifestData});
103+
}
59104
let packageName = safeFileName(
60-
`${manifestData.name}-${manifestData.version}.zip`);
105+
`${extensionName}-${manifestData.version}.zip`);
61106
let extensionPath = path.join(artifactsDir, packageName);
62107
let stream = createWriteStream(extensionPath);
63108

src/util/manifest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const log = createLogger(__filename);
1414
export type ExtensionManifest = {
1515
name: string,
1616
version: string,
17+
default_locale?: string,
1718
};
1819

1920
export default async function getValidatedManifest(
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extensionName": {
3+
"message": "Name of the extension",
4+
"description": "This is an example of a minimal extension that does nothing"
5+
}
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* eslint no-console:0 */
2+
3+
console.log('background script loaded');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"manifest_version": 2,
3+
"name": "__MSG_extensionName__",
4+
"description": "__MSG_extensionDescription__",
5+
"version": "1.0",
6+
"default_locale": "en"
7+
}

tests/unit/test-cmd/test.build.js

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,21 @@ import {it, describe} from 'mocha';
55
import {assert} from 'chai';
66
import sinon from 'sinon';
77

8-
import build, {safeFileName, FileFilter} from '../../../src/cmd/build';
8+
import build, {
9+
safeFileName,
10+
FileFilter,
11+
getDefaultLocalizedName,
12+
} from '../../../src/cmd/build';
913
import {withTempDir} from '../../../src/util/temp-dir';
1014
import {fixturePath, makeSureItFails, ZipFile} from '../helpers';
11-
import {basicManifest, manifestWithoutApps} from '../test-util/test.manifest';
15+
import {
16+
basicManifest,
17+
manifestWithoutApps,
18+
} from '../test-util/test.manifest';
19+
import {UsageError} from '../../../src/errors';
20+
import {createLogger} from '../../../src/util/logger';
1221

22+
const log = createLogger(__filename);
1323

1424
describe('build', () => {
1525

@@ -37,6 +47,113 @@ describe('build', () => {
3747
);
3848
});
3949

50+
it('gives the correct name to a localized extension', () => {
51+
return withTempDir(
52+
(tmpDir) =>
53+
build({
54+
sourceDir: fixturePath('minimal-localizable-web-ext'),
55+
artifactsDir: tmpDir.path(),
56+
})
57+
.then((buildResult) => {
58+
assert.match(buildResult.extensionPath,
59+
/name_of_the_extension-1\.0\.zip$/);
60+
return buildResult.extensionPath;
61+
})
62+
);
63+
});
64+
65+
it('handles repeating localization keys', () => {
66+
return withTempDir(
67+
(tmpDir) => {
68+
const messageFileName = path.join(tmpDir.path(), 'messages.json');
69+
fs.writeFileSync(messageFileName,
70+
`{"extensionName": {
71+
"message": "example extension",
72+
"description": "example description"
73+
}
74+
}`);
75+
76+
const manifestWithRepeatingPattern = {
77+
name: '__MSG_extensionName__ __MSG_extensionName__',
78+
version: '0.0.1',
79+
};
80+
81+
return getDefaultLocalizedName({
82+
messageFile: messageFileName,
83+
manifestData: manifestWithRepeatingPattern,
84+
})
85+
.then((result) => {
86+
assert.match(result, /example extension example extension/);
87+
});
88+
}
89+
);
90+
});
91+
92+
it('checks locale file for malformed json', () => {
93+
return withTempDir(
94+
(tmpDir) => {
95+
const messageFileName = path.join(tmpDir.path(), 'messages.json');
96+
fs.writeFileSync(messageFileName,
97+
'{"simulated:" "json syntax error"');
98+
return getDefaultLocalizedName({
99+
messageFile: messageFileName,
100+
manifestData: manifestWithoutApps,
101+
})
102+
.then(makeSureItFails())
103+
.catch((error) => {
104+
assert.instanceOf(error, UsageError);
105+
assert.match(
106+
error.message,
107+
/Error .* file .*messages\.json: SyntaxError: Unexpected string/);
108+
});
109+
}
110+
);
111+
});
112+
113+
it('checks locale file for incorrect format', () => {
114+
return withTempDir(
115+
(tmpDir) => {
116+
const messageFileName = path.join(tmpDir.path(), 'messages.json');
117+
//This is missing the 'message' key
118+
fs.writeFileSync(messageFileName,
119+
`{"extensionName": {
120+
"description": "example extension"
121+
}
122+
}`);
123+
const basicLocalizedManifest = {
124+
name: '__MSG_extensionName__',
125+
version: '0.0.1',
126+
};
127+
return getDefaultLocalizedName({
128+
messageFile: messageFileName,
129+
manifestData: basicLocalizedManifest,
130+
})
131+
.then(makeSureItFails())
132+
.catch((error) => {
133+
assert.instanceOf(error, UsageError);
134+
assert.match(
135+
error.message,
136+
/The locale file .*messages\.json is missing key: extensionName/);
137+
});
138+
}
139+
);
140+
});
141+
142+
it('throws an error if the locale file does not exist', () => {
143+
return getDefaultLocalizedName({
144+
messageFile: '/path/to/non-existent-dir/messages.json',
145+
manifestData: manifestWithoutApps,
146+
})
147+
.then(makeSureItFails())
148+
.catch((error) => {
149+
log.info(error);
150+
assert.instanceOf(error, UsageError);
151+
assert.match(
152+
error.message,
153+
/Error .* file .*messages\.json: .*: no such file or directory/);
154+
});
155+
});
156+
40157
it('can build an extension without an ID', () => {
41158
return withTempDir(
42159
(tmpDir) => {

0 commit comments

Comments
 (0)