Skip to content

Commit

Permalink
feat: warn user/designer about missing property
Browse files Browse the repository at this point in the history
  • Loading branch information
3cp committed Jun 9, 2022
1 parent 72a0c79 commit 6716fd8
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 69 deletions.
14 changes: 11 additions & 3 deletions lib/preprocess/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { codeFrameColumns } = require('@babel/code-frame');
const {html, js} = require('./regex-rules');
const applicable = require('../applicable');
const { warn } = require('../log');

class Token {
constructor(start, end, value) {
Expand Down Expand Up @@ -171,7 +172,7 @@ function dangerousEval(expression, properties) {

// properties is like {name: 'app-name', foo: 'lorem'}
// features is like ['aurelia', 'typescript', 'jest']
module.exports = function(source, properties, features, mode) {
module.exports = function(filePath, source, properties, features, mode) {
const rules = mode === 'html' ? html : js;
// normalize line break
let rv = source.replace(/(?:\r\n)|\r/g, '\n');
Expand All @@ -180,8 +181,15 @@ module.exports = function(source, properties, features, mode) {
applicable(features, expression) ? recurse(inside) : ''
);

rv = replace(rv, rules.echo, (_, variable) => properties[variable] || '');
rv = replace(rv, rules.echo, (_, variable) => {
if (properties.hasOwnProperty(variable)) {
return properties[variable] || '';
} else {
warn(`Property "${variable}" is not defined for use in @echo in template ${filePath}`);
return '';
}
});
rv = replace(rv, rules.eval, (_, expression) => dangerousEval(expression, properties));

return rv;
};
};
2 changes: 1 addition & 1 deletion lib/write-project/3-preprocess.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = function(properties, features) {
const ext = file.extname.toLowerCase();
const htmlMode = xmlExts.includes(ext);
const contents = file.contents.toString();
const newContents = preprocess(contents, properties, features, htmlMode ? 'html' : 'js');
const newContents = preprocess(file.relative, contents, properties, features, htmlMode ? 'html' : 'js');
if (contents !== newContents) {
file.contents = Buffer.from(newContents);
}
Expand Down
22 changes: 11 additions & 11 deletions test/preprocess/echo.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,62 @@ const test = require('ava');
const preprocess = require('../../lib/preprocess');

test('preprocess can handle empty input', t => {
t.is(preprocess('', {}, null, 'html'), '');
t.is(preprocess('', {}, null), '');
t.is(preprocess('a/b.html', '', {}, null, 'html'), '');
t.is(preprocess('a/b.js', '', {}, null), '');
});

test('@echo resolves and echoes variable in html syntax', t => {
t.is(
preprocess('a<!-- @echo FINGERPRINT -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!-- @echo FINGERPRINT -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
});

test('@echo resolves and echoes variable (block comment) in js syntax', t => {
t.is(
preprocess('a/* @echo FINGERPRINT */c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/* @echo FINGERPRINT */c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
});

test('@echo resolves and echoes variable (line comment) in js syntax', t => {
t.is(
preprocess('a\n// @echo FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n// @echo FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
});

test('multiple @echo without overreaching in js syntax', t => {
t.is(
preprocess('a/* @echo FOO */b/* @echo BAR */c', {FOO: 1, BAR: 2}),
preprocess('a/b.js', 'a/* @echo FOO */b/* @echo BAR */c', {FOO: 1, BAR: 2}),
'a1b2c'
);
});

test('@echo allows ommitting of whitespaces before and after @echo in html syntax', t => {
t.is(
preprocess('a<!--@echo FINGERPRINT-->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!--@echo FINGERPRINT-->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
});

test('@echo allows ommitting of whitespaces before and after @echo (block comment) in js syntax', t => {
t.is(
preprocess('a/*@echo FINGERPRINT*/c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/*@echo FINGERPRINT*/c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
});

test('@echo allows ommitting of whitespaces before and after @echo (line comment) in js syntax', t => {
t.is(
preprocess('a\n//@echo FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n//@echo FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
});

test('@echo shows missing property as empty string', t => {
t.is(
preprocess('a/*@echo FINGERPRINT*/c', {}),
preprocess('a/b.js', 'a/*@echo FINGERPRINT*/c', {}),
'ac'
);
});
});
38 changes: 19 additions & 19 deletions test/preprocess/eval.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,97 +3,97 @@ const preprocess = require('../../lib/preprocess');

test('@eval resolves and evals variable in html syntax', t => {
t.is(
preprocess('a<!-- @eval FINGERPRINT -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!-- @eval FINGERPRINT -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
t.is(
preprocess('a<!-- @eval FINGERPRINT.toLowerCase() -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!-- @eval FINGERPRINT.toLowerCase() -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xdeadbeefc'
);
});

test('@eval resolves and evals variable (block comment) in js syntax', t => {
t.is(
preprocess('a/* @eval FINGERPRINT */c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/* @eval FINGERPRINT */c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
t.is(
preprocess('a/* @eval JSON.stringify(FINGERPRINT) */c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/* @eval JSON.stringify(FINGERPRINT) */c', {FINGERPRINT: '0xDEADBEEF'}),
'a"0xDEADBEEF"c'
);
});

test('@eval resolves and evals variable (line comment) in js syntax', t => {
t.is(
preprocess('a\n// @eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n// @eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
t.is(
preprocess('a\n// @eval FINGERPRINT.slice(2, 4)\nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n// @eval FINGERPRINT.slice(2, 4)\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\nDE\nc'
);
});

test('multiple @eval without overreaching in js syntax', t => {
t.is(
preprocess('a/* @eval FOO */b/* @eval BAR */c', {FOO: 1, BAR: 2}),
preprocess('a/b.js', 'a/* @eval FOO */b/* @eval BAR */c', {FOO: 1, BAR: 2}),
'a1b2c'
);
t.is(
preprocess('a/* @eval FOO + 10 */b/* @eval BAR * 2 */c', {FOO: 1, BAR: 2}),
preprocess('a/b.js', 'a/* @eval FOO + 10 */b/* @eval BAR * 2 */c', {FOO: 1, BAR: 2}),
'a11b4c'
);
});

test('@eval allows ommitting of whitespaces before and after @eval in html syntax', t => {
t.is(
preprocess('a<!--@eval FINGERPRINT-->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!--@eval FINGERPRINT-->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
t.is(
preprocess('a<!--@eval FINGERPRINT + \'#\' -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
preprocess('a/b.html', 'a<!--@eval FINGERPRINT + \'#\' -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEF#c'
);
});

test('@eval allows ommitting of whitespaces before and after @eval (block comment) in js syntax', t => {
t.is(
preprocess('a/*@eval FINGERPRINT*/c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/*@eval FINGERPRINT*/c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
t.is(
preprocess('a/*@eval FINGERPRINT.toLowerCase()*/c', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a/*@eval FINGERPRINT.toLowerCase()*/c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xdeadbeefc'
);
});

test('@eval allows ommitting of whitespaces before and after @eval (line comment) in js syntax', t => {
t.is(
preprocess('a\n//@eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n//@eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
t.is(
preprocess('a\n//@eval FINGERPRINT + "##" \nc', {FINGERPRINT: '0xDEADBEEF'}),
preprocess('a/b.js', 'a\n//@eval FINGERPRINT + "##" \nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF##\nc'
);
});

test('@eval complains about broken js syntax', t => {
t.throws(() => preprocess('a\n//@eval FINGERPRINT+\nc', {FINGERPRINT: '0xDEADBEEF'}));
t.throws(() => preprocess('a/b.js', 'a\n//@eval FINGERPRINT+\nc', {FINGERPRINT: '0xDEADBEEF'}));
});

test('@eval treats undefined/null/NaN as empty string', t => {
t.is(
preprocess('a\n//@eval undefined\nc', {}),
preprocess('a/b.js', 'a\n//@eval undefined\nc', {}),
'a\n\nc'
);
t.is(
preprocess('a\n//@eval null\nc', {}),
preprocess('a/b.js', 'a\n//@eval null\nc', {}),
'a\n\nc'
);
// NaN
t.is(
preprocess('a\n//@eval 0 / 0 \nc', {}),
preprocess('a/b.js', 'a\n//@eval 0 / 0 \nc', {}),
'a\n\nc'
);
});
});

0 comments on commit 6716fd8

Please sign in to comment.