Skip to content

Commit

Permalink
feat: STRF-10101 Check translation rows compilation on stencil bundle…
Browse files Browse the repository at this point in the history
…/start (#1145)
  • Loading branch information
jairo-bc authored Nov 7, 2023
1 parent 3abd456 commit 92b760a
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 1 deletion.
7 changes: 7 additions & 0 deletions lib/bundle-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const privateThemeConfigValidationSchema = require('./schemas/privateThemeConfig
const themeConfigValidationSchema = require('./schemas/themeConfig.json');
const themeValidationSchema = require('./schemas/themeSchema.json');
const ScssValidator = require('./ScssValidator');
const LangHelpersValidator = require('./lang/validator');

const VALID_IMAGE_TYPES = ['.jpg', '.jpeg', '.png', '.gif'];
const WIDTH_COMPOSED = 600;
Expand Down Expand Up @@ -42,6 +43,7 @@ class BundleValidator {
this.objectsToValidate = ['head.scripts', 'footer.scripts'];
this.jsonSchemaValidatorOptions = { schemaId: 'auto', allErrors: true };
this.scssValidator = new ScssValidator(themePath, themeConfig);
this.langHelpersValidator = new LangHelpersValidator(themePath, themeConfig);

// Array of tasks used in async.series
this.validationTasks = [
Expand All @@ -50,6 +52,7 @@ class BundleValidator {
this._validateSchemaTranslations.bind(this),
this._validateTemplatesFrontmatter.bind(this),
this._validateCssFiles.bind(this),
this._validateLangFiles.bind(this),
];

if (!this.isPrivate) {
Expand Down Expand Up @@ -405,6 +408,10 @@ class BundleValidator {
return true;
}

async _validateLangFiles() {
await this.langHelpersValidator.run();
}

async _validateCssFiles() {
await this.scssValidator.run();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bundle-validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('BundleValidator', () => {

const res = await promisify(validator.validateTheme.bind(validator))();

expect(res).toHaveLength(6); // 6 validation tasks
expect(res).toHaveLength(7); // 7 validation tasks
expect(res).not.toContain(false);
});

Expand Down
113 changes: 113 additions & 0 deletions lib/lang/validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
require('colors');
const fs = require('fs');
const path = require('path');
const { recursiveReadDir } = require('../utils/fsUtils');

const LANG_HELPER_REGEXP = /{{\s*lang\s*(?:'|")((?:\w*(?:-\w*)*(\.\w*(?:-\w*)*)*)+)/gim;

class LangpathsValidator {
/**
*
* @param {String} themePath
*/
constructor(themePath) {
this.themePath = themePath;
}

async run(defaultLang = null) {
const templatesPath = path.join(this.themePath, 'templates');
const paths = await this.getLangHelpersPaths(templatesPath);
const dedupePaths = [...new Set(paths)];
const langFiles = await this.getLangFilesContent(defaultLang);
const errors = this.validate(dedupePaths, langFiles);
this.printErrors(errors);
return errors;
}

printErrors(errors) {
if (errors.length > 0) {
console.log(
'Warning: Your theme has some missing translations used in the theme:'.yellow,
);
console.log(errors.join('\n').yellow);
}
}

searchLangPaths(fileContent, langPath) {
const keys = langPath.split('.');
let value = fileContent;

for (const key of keys) {
// eslint-disable-next-line no-prototype-builtins
if (value && value.hasOwnProperty(key)) {
value = value[key];
} else {
return false;
}
}

return value;
}

validate(paths, langFiles) {
const errors = [
...this.checkLangFiles(langFiles),
...this.checkForMissingTranslations(paths, langFiles),
];
return errors;
}

checkForMissingTranslations(paths, langFiles) {
const errors = [];
for (const langPath of paths) {
// eslint-disable-next-line no-restricted-syntax,guard-for-in
for (const langFile in langFiles) {
const translation = this.searchLangPaths(langFiles[langFile], langPath);
if (!translation) {
errors.push(`Missing translation for ${langPath} in ${langFile}`);
}
}
}
return errors;
}

checkLangFiles(files) {
if (files.length === 0) {
return ['No lang files found in your theme'];
}
return [];
}

async getLangHelpersPaths(templatesPath) {
const files = await recursiveReadDir(templatesPath);
const paths = [];
for await (const file of files) {
const content = await fs.promises.readFile(file, { encoding: 'utf-8' });
const result = content.matchAll(LANG_HELPER_REGEXP);
const arr = [...result];
if (arr.length > 0) {
const langPath = arr[0][1];
paths.push(langPath);
}
}
return paths;
}

async getLangFilesContent(defaultLang = null) {
const filesContent = {};
const langPath = path.join(this.themePath, 'lang');
let files = await recursiveReadDir(langPath);

if (defaultLang) {
files = files.filter((file) => file.includes(defaultLang));
}

for await (const file of files) {
const content = await fs.promises.readFile(file, { encoding: 'utf-8' });
filesContent[file] = JSON.parse(content);
}
return filesContent;
}
}

module.exports = LangpathsValidator;
37 changes: 37 additions & 0 deletions lib/lang/validator.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const path = require('path');

const LangFilesValidator = require('./validator');

describe('lang/validator.js tests', () => {
afterEach(() => {
jest.restoreAllMocks();
});

describe('valid', () => {
it('run with no errors', async () => {
const themePath = path.join(process.cwd(), 'test/_mocks/themes/valid');
const validator = new LangFilesValidator(themePath);
const errors = await validator.run();

expect(errors).toHaveLength(0);
});

it('run with no errors providing default lang', async () => {
const themePath = path.join(process.cwd(), 'test/_mocks/themes/valid');
const validator = new LangFilesValidator(themePath);
const errors = await validator.run('en');

expect(errors).toHaveLength(0);
});
});

describe('not valid', () => {
it('run with lang helper that is not presented in lang file', async () => {
const themePath = path.join(process.cwd(), 'test/_mocks/themes/invalid-translations');
const validator = new LangFilesValidator(themePath);
const errors = await validator.run();

expect(errors).toHaveLength(1);
});
});
});
4 changes: 4 additions & 0 deletions lib/stencil-start.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const cliCommonModule = require('./cliCommon');
const themeApiClientModule = require('./theme-api-client');
const storeSettingsApiClientModule = require('./store-settings-api-client');
const LangHelper = require('./lang-helper');
const LangValidator = require('./lang/validator');

class StencilStart {
constructor({
Expand All @@ -32,6 +33,7 @@ class StencilStart {
CyclesDetector = Cycles,
stencilPushUtils = stencilPushUtilsModule,
logger = console,
langValidator = new LangValidator(THEME_PATH),
} = {}) {
this._browserSync = browserSync;
this._themeApiClient = themeApiClient;
Expand All @@ -46,6 +48,7 @@ class StencilStart {
this._CyclesDetector = CyclesDetector;
this._stencilPushUtils = stencilPushUtils;
this._logger = logger;
this._langValidator = langValidator;
}

async run(cliOptions) {
Expand Down Expand Up @@ -289,6 +292,7 @@ class StencilStart {
} else {
try {
await this._langHelper.checkLangKeysPresence(filesPaths, defaultShopperLanguage);
await this._langValidator.run(defaultShopperLanguage);
} catch (e) {
this._logger.error(e);
}
Expand Down
20 changes: 20 additions & 0 deletions test/_mocks/themes/invalid-translations/lang/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"header": {
"welcome_back": "Welcome back, {name}"
},
"footer": {
"brands": "Popular Brands",
"navigate": "Navigate",
"info": "Info",
"categories": "Categories",
"call_us": "Call us at {phone_number}"
},
"home": {
"heading": "Home"
},
"blog": {
"recent_posts": "Recent Posts",
"label": "Blog",
"posted_by": "Posted by {name}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
b


<!-- test
{{{stylesheet '/assets/custom/css/test.css'}}} -->

more text here
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>page.html</title>
</head>
<body>
{{ lang 'failed' }}
</body>
</html>
12 changes: 12 additions & 0 deletions test/_mocks/themes/invalid-translations/templates/pages/page2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>page2.html</title>
{{head.scripts}}
</head>
<body>
<h1>{{theme_settings.customizable_title}}</h1>
{{> components/b}}
{{footer.scripts}}
</body>
</html>

0 comments on commit 92b760a

Please sign in to comment.