UNPKG

chrome-devtools-frontend

Version:
790 lines (705 loc) • 22.9 kB
// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as FormatterWorker from './formatter_worker.js'; function formatJavaScript(text: string): string { return FormatterWorker.FormatterWorker.format('text/javascript', text, ' ').content; } describe('JavaScriptFormatter', () => { it('formats await expressions correctly', () => { const formattedCode = formatJavaScript('(async () => { await someFunctionThatNeedsAwaiting(); callSomeOtherFunction(); })();'); assert.strictEqual( formattedCode, '(async () => {\n await someFunctionThatNeedsAwaiting();\n callSomeOtherFunction();\n}\n)();\n'); }); it('formats async-function expressions correctly', () => { const formattedCode = formatJavaScript('async function foo() {return await Promise.resolve(1);}'); assert.strictEqual(formattedCode, `async function foo() { return await Promise.resolve(1); } `); }); it('formats top-level await correctly', () => { const formattedCode = formatJavaScript('const myFile=await import(\n"my-file.mjs");'); assert.strictEqual(formattedCode, 'const myFile = await import("my-file.mjs");\n'); }); it('formats identifiers containing escaped characters correctly', () => { const formattedCode = formatJavaScript(String.raw`const x=42;let \u0275_escaped;`); assert.strictEqual(formattedCode, 'const x = 42;\nlet \\u0275_escaped;\n'); }); it('formats nullish coalescing expressions correctly', () => { const formattedCode = formatJavaScript('false??true'); assert.strictEqual(formattedCode, 'false ?? true\n'); }); it('formats optional chaining expressions correctly', () => { const formattedCode = formatJavaScript('var x=a?.b;'); assert.strictEqual(formattedCode, 'var x = a?.b;\n'); }); it('formats logical assignment expressions correctly', () => { const formattedCode = formatJavaScript('x||=1;'); assert.strictEqual(formattedCode, 'x ||= 1;\n'); }); it('formats numeric separators correctly', () => { const formattedCode = formatJavaScript('x=1_000;'); assert.strictEqual(formattedCode, 'x = 1_000;\n'); }); it('formats do-while loops correctly', () => { const formattedCode = formatJavaScript(`function demo() { do {} while (false); if (true) {} } function demo() {do {} while (false);if (true) {}}`); assert.strictEqual(formattedCode, `function demo() { do {} while (false); if (true) {} } function demo() { do {} while (false); if (true) {} } `); }); it('formats while loops correctly', () => { const formattedCode = formatJavaScript('while(true){print(\'infinity\');}'); assert.strictEqual(formattedCode, `while (true) { print(\'infinity\'); } `); }); it('formats function statements correctly', () => { const formattedCode = formatJavaScript('function test(a,b,c){a*=b;return c+a;}'); assert.strictEqual(formattedCode, `function test(a, b, c) { a *= b; return c + a; } `); }); it('formats variable statements correctly', () => { const formattedCode = formatJavaScript('var a=1,b={},c=2,d="hello world";var a,b,c,d=2,e,f=3;var a={};'); assert.strictEqual(formattedCode, `var a = 1 , b = {} , c = 2 , d = "hello world"; var a, b, c, d = 2, e, f = 3; var a = {}; `); }); it('formats array-literals correctly', () => { const formattedCode = formatJavaScript('var arr=[3,2,1,0]'); assert.strictEqual(formattedCode, `var arr = [3, 2, 1, 0] `); }); it('formats ternary expressions correctly', () => { const formattedCode = formatJavaScript('a>b?a:b'); assert.strictEqual(formattedCode, `a > b ? a : b `); }); it('formats labeled statements correctly', () => { const formattedCode = formatJavaScript('firstLoop:while(true){break firstLoop;continue firstLoop;}'); assert.strictEqual(formattedCode, `firstLoop: while (true) { break firstLoop; continue firstLoop; } `); }); it('formats multiple statements on the same line correctly', () => { const formattedCode = formatJavaScript('rebuild(),show(),hasNew?refresh():noop();'); assert.strictEqual(formattedCode, `rebuild(), show(), hasNew ? refresh() : noop(); `); }); it('formats if-statements correctly', () => { const formattedCode = formatJavaScript( 'if(a<b)log(a);else log(b);if(a<b){log(a)}else{log(b);}if(a===b)log(\'equals\');if(a!==b){log(\'non-eq\');}if(a>b&&b>c){print(a);print(b);}'); assert.strictEqual(formattedCode, `if (a < b) log(a); else log(b); if (a < b) { log(a) } else { log(b); } if (a === b) log('equals'); if (a !== b) { log('non-eq'); } if (a > b && b > c) { print(a); print(b); } `); }); it('formats break- and continue-statements in for-loops correctly', () => { const formattedCode = formatJavaScript(`for(var i in set)if(i%2===0)break;else continue; function foo(){while(1){if (a)continue;test();}}`); assert.strictEqual(formattedCode, `for (var i in set) if (i % 2 === 0) break; else continue; function foo() { while (1) { if (a) continue; test(); } } `); }); it('formats null-keyword correctly', () => { const formattedCode = formatJavaScript('1||null;'); assert.strictEqual(formattedCode, `1 || null; `); }); it('formats exponential operator correctly', () => { const formattedCode = formatJavaScript('2**3'); assert.strictEqual(formattedCode, `2 ** 3 `); }); it('formats for-loops correctly', () => { const formattedCode = formatJavaScript( 'for(var value of map)if (value.length%3===0)console.log(value);for(var key in myMap)print(key);for(var value of myMap)print(value);'); assert.strictEqual(formattedCode, `for (var value of map) if (value.length % 3 === 0) console.log(value); for (var key in myMap) print(key); for (var value of myMap) print(value); `); }); it('formats chained and nested if-statements correctly', () => { const formattedCode = formatJavaScript( 'if(a%7===0)b=1;else if(a%9===1) b = 2;else if(a%5===3){b=a/2;b++;} else b= 3;{if (a>b){a();pretty();}else if (a+b)e();reset();}'); assert.strictEqual(formattedCode, `if (a % 7 === 0) b = 1; else if (a % 9 === 1) b = 2; else if (a % 5 === 3) { b = a / 2; b++; } else b = 3; { if (a > b) { a(); pretty(); } else if (a + b) e(); reset(); } `); }); it('formats try-catch statements correctly', () => { const formattedCode = formatJavaScript('try{a(b());}catch(e){f()}finally{f();}'); assert.strictEqual(formattedCode, `try { a(b()); } catch (e) { f() } finally { f(); } `); }); it('formats object-spreads correctly', () => { const formattedCode = formatJavaScript('const a = {a:4,...{a: 5,b: 42}};'); assert.strictEqual(formattedCode, `const a = { a: 4, ...{ a: 5, b: 42 } }; `); }); it('formats object destructuring correctly', () => { const formattedCode = formatJavaScript('let{x,y}=getXYFromTouchOrPointer(e);var test = function({x,y}){foo(x,y);}'); assert.strictEqual(formattedCode, `let {x, y} = getXYFromTouchOrPointer(e); var test = function({x, y}) { foo(x, y); } `); }); it('formats let declaration for "$" correctly', () => { const formattedCode = formatJavaScript('let $=1;'); assert.strictEqual(formattedCode, 'let $ = 1;\n'); }); it('formats let declaration for "_" correctly', () => { const formattedCode = formatJavaScript('let _=1;'); assert.strictEqual(formattedCode, 'let _ = 1;\n'); }); it('formats let declaration for unicode name correctly', () => { const formattedCode = formatJavaScript('let \u00e1=1;'); assert.strictEqual(formattedCode, 'let \u00e1 = 1;\n'); }); it('formats const declaration for unicode name correctly', () => { const formattedCode = formatJavaScript('const \u00e1=1;'); assert.strictEqual(formattedCode, 'const \u00e1 = 1;\n'); }); it('formats yield <number> correctly', () => { const formattedCode = formatJavaScript('function *one() {yield 1;}'); assert.strictEqual(formattedCode, `function *one() { yield 1; } `); }); it('formats object-expressions correctly', () => { const formattedCode = formatJavaScript( 'var mapping={original:[1,2,3],formatted:[],count:0};var obj={\'foo\':1,bar:"2",cat:{dog:\'1989\'}}'); assert.strictEqual(formattedCode, `var mapping = { original: [1, 2, 3], formatted: [], count: 0 }; var obj = { 'foo': 1, bar: "2", cat: { dog: '1989' } } `); }); it('formats block-statements correctly', () => { const formattedCode = formatJavaScript('{ print(1); print(2); }'); assert.strictEqual(formattedCode, `{ print(1); print(2); } `); }); it('formats assignment expressions correctly', () => { const formattedCode = formatJavaScript('var exp=\'a string\';c=+a+(0>a?b:0);c=(1);var a=(1);'); assert.strictEqual(formattedCode, `var exp = 'a string'; c = +a + (0 > a ? b : 0); c = (1); var a = (1); `); }); it('formats with-statements correctly', () => { const formattedCode = formatJavaScript('with(obj)log(\'first\');with(nice){log(1);log(2);}done();'); assert.strictEqual(formattedCode, `with (obj) log(\'first\'); with (nice) { log(1); log(2); } done(); `); }); it('formats switch-statements correctly', () => { const formattedCode = formatJavaScript( 'switch (a) { case 1, 3: log("odd");break;case 2:log("even");break;case 42:case 89: log(a);default:log("interesting");log(a);}log("done");'); assert.strictEqual(formattedCode, `switch (a) { case 1, 3: log("odd"); break; case 2: log("even"); break; case 42: case 89: log(a); default: log("interesting"); log(a); } log("done"); `); }); it('formats generator-expressions correctly', () => { const formattedCode = formatJavaScript('function *max(){var a=yield;var b=yield 10;if(a>b)return a;else return b;}'); assert.strictEqual(formattedCode, `function *max() { var a = yield; var b = yield 10; if (a > b) return a; else return b; } `); }); it('formats block-comments correctly', () => { const formattedCode = formatJavaScript(`/** this * is * block * comment */ var a = 10;`); assert.strictEqual(formattedCode, `/** this * is * block * comment */ var a = 10; `); }); it('formats let-assignments correctly', () => { const formattedCode = formatJavaScript('for(var i=0;i<names.length;++i){let name=names[i];let person=persons[i];}'); assert.strictEqual(formattedCode, `for (var i = 0; i < names.length; ++i) { let name = names[i]; let person = persons[i]; } `); }); it('formats anonymous functions correctly', () => { const formattedCode = formatJavaScript(`setTimeout(function(){alert(1);},2000); function test(arg){console.log(arg);}test(a=>a+2); var onClick = function() { console.log(\'click!\'); };console.log(\'done\');var onStart = function() { a(); }, onFinish = function() { b(); }; var onStart = function() {}, delay=1000, belay=document.activeElement;`); assert.strictEqual(formattedCode, `setTimeout(function() { alert(1); }, 2000); function test(arg) { console.log(arg); } test(a => a + 2); var onClick = function() { console.log('click!'); }; console.log('done'); var onStart = function() { a(); } , onFinish = function() { b(); }; var onStart = function() {} , delay = 1000 , belay = document.activeElement; `); }); it('formats arrow functions correctly', () => { const formattedCode1 = formatJavaScript('const double=x=>x*2;'); const formattedCode2 = formatJavaScript('const sum=(a,b)=>a+b;'); const formattedCode3 = formatJavaScript('const double=x=>{return x*2;}'); const formattedCode4 = formatJavaScript('const sum=(a,b,c)=>{const val=a+b+c;return val;}'); assert.strictEqual(formattedCode1, `const double = x => x * 2; `); assert.strictEqual(formattedCode2, `const sum = (a, b) => a + b; `); assert.strictEqual(formattedCode3, `const double = x => { return x * 2; } `); assert.strictEqual(formattedCode4, `const sum = (a, b, c) => { const val = a + b + c; return val; } `); }); describe('formats files with comments', () => { it('handles 1 leading comment correctly', () => { const formattedCode = formatJavaScript(`// This is a starting comment console.log('5');`); assert.strictEqual(formattedCode, `// This is a starting comment console.log('5'); `); }); it('handles leading hashbang correctly', () => { const formattedCode = formatJavaScript(`#! hashbang {{{console.log(1)}}}`); assert.strictEqual(formattedCode, `#! hashbang { { { console.log(1) } } } `); }); it('handles 1 trailing comment correctly', () => { const formattedCode = formatJavaScript('console.log(\'5\'); // This is a trailing comment'); assert.strictEqual(formattedCode, `console.log('5'); // This is a trailing comment `); }); it('handles 1 trailing comment on a new line correctly', () => { const formattedCode = formatJavaScript(`console.log('5'); // This is a new line comment`); assert.strictEqual(formattedCode, `console.log('5'); // This is a new line comment `); }); it('handles 2 leading comments', () => { const formattedCode = formatJavaScript(`// This is a starting line comment /* This is a starting block comment */ console.log('5');`); assert.strictEqual(formattedCode, `// This is a starting line comment /* This is a starting block comment */ console.log('5'); `); }); it('handles 2 trailing comments correctly', () => { const formattedCode = formatJavaScript(`console.log('5'); // This is a trailing comment same line // This is a trailing new line comment`); assert.strictEqual(formattedCode, `console.log('5'); // This is a trailing comment same line // This is a trailing new line comment `); }); it('handles leading and trailing comments correctly', () => { const formattedCode = formatJavaScript(`// This is a starting line comment /* This is a starting block comment */ console.log('5'); // This is a trailing comment same line // This is a trailing new line comment`); assert.strictEqual(formattedCode, `// This is a starting line comment /* This is a starting block comment */ console.log('5'); // This is a trailing comment same line // This is a trailing new line comment `); }); it('handles a hashbang, leading and trailing comments correctly', () => { const formattedCode = formatJavaScript(`#! hashbang // This is a starting line comment /* This is a starting block comment */ console.log('5'); // This is a trailing comment same line // This is a trailing new line comment`); assert.strictEqual(formattedCode, `#! hashbang // This is a starting line comment /* This is a starting block comment */ console.log('5'); // This is a trailing comment same line // This is a trailing new line comment `); }); }); describe('formats files with classes', () => { it('handles an empty class correctly', () => { const formattedCode = formatJavaScript('class Test{}'); assert.strictEqual(formattedCode, `class Test { } `); }); it('handles an empty constructor correctly', () => { const formattedCode = formatJavaScript('class Test{constructor(){}}'); assert.strictEqual(formattedCode, `class Test { constructor() {} } `); }); it('handles a single method correctly', () => { const formattedCode = formatJavaScript('class Test{constructor(){this.bar=10;}givemebar(){return this.bar;}}'); assert.strictEqual(formattedCode, `class Test { constructor() { this.bar = 10; } givemebar() { return this.bar; } } `); }); it('handles extending a super class correctly', () => { const formattedCode = formatJavaScript('class Foo extends Bar{constructor(name){super(name);}getName(){return super.getName();}}'); assert.strictEqual(formattedCode, `class Foo extends Bar { constructor(name) { super(name); } getName() { return super.getName(); } } `); }); it('handles consecutive class declarations correctly', () => { const formattedCode = formatJavaScript('class A{}class B extends A{constructor(){super();}}'); assert.strictEqual(formattedCode, `class A { } class B extends A { constructor() { super(); } } `); }); it('handles static methods correctly', () => { const formattedCode = formatJavaScript( 'class Employer{static count(){this._counter = (this._counter || 0) + 1; return this._counter;}}'); assert.strictEqual(formattedCode, `class Employer { static count() { this._counter = (this._counter || 0) + 1; return this._counter; } } `); }); it('handles class expressions correctly', () => { const formattedCode = formatJavaScript('new(class{constructor(){debugger}})'); assert.strictEqual(formattedCode, `new (class { constructor() { debugger } } ) `); }); }); describe('formats files with template literals', () => { it('handles simple template literal correctly', () => { const formattedCode = formatJavaScript('var foo = `bar`;'); assert.strictEqual(formattedCode, `var foo = \`bar\`; `); }); it('handles multi-line template literals correctly', () => { const formattedCode = formatJavaScript(`var foo = \`this bar\`;`); assert.strictEqual(formattedCode, `var foo = \`this bar\`; `); }); it('handles string substitution in template literals correctly', () => { const formattedCode = formatJavaScript(`var a=\`I have \${credit+cash}$\`; var a=\`\${name} has \${credit+cash}\${currency?currency:"$"}\`;`); assert.strictEqual(formattedCode, `var a = \`I have \${credit + cash}$\`; var a = \`\${name} has \${credit + cash}\${currency ? currency : "$"}\`; `); }); it('handles tagged template literals correctly', () => { const formattedCode = formatJavaScript('escapeHtml`<div class=${classnName} width=${a+b}/>`;'); assert.strictEqual(formattedCode, `escapeHtml\`<div class=\${classnName} width=\${a + b}/>\`; `); }); }); it('removes consecutive new lines', () => { const formattedCode = formatJavaScript(`a(); b();`); assert.strictEqual(formattedCode, `a(); b(); `); }); it('formats expressions in parentheses correctly', () => { const formattedCode = formatJavaScript('if((a))((b));else (c);'); assert.strictEqual(formattedCode, `if ((a)) ((b)); else (c); `); }); it('formats obfuscated code', () => { const formattedCode = formatJavaScript(`function formatted1() { var variable1 = 0; } function withComments() { // comment return "functionWithComments"; } try{onmessage=function(event){var source=event.data;var formattedSource=beautify(source);var mapping=buildMapping(source,formattedSource);postMessage({formattedSource:formattedSource,mapping:mapping})};function beautify(source){var ast=parse.parse(source);var beautifyOptions= {indent_level:4,indent_start:0,quote_keys:false,space_colon:false};return process.gen_code(ast,beautifyOptions)}function buildMapping(source,formattedSource){var mapping={original:[],formatted:[]};var lastPosition=0;var regexp=/(^|[^\\])\b((?=\D)[\$\.\w]+)\b/g;while(true) {var match=regexp.exec(formattedSource);if(!match)break;var position=source.indexOf(match[2],lastPosition);if(position===-1)throw"No match found in original source for "+match[2];mapping.original.push(position);mapping.formatted.push(match.index+match[1].length); lastPosition=position+match[2].length}return mapping}function require(){return parse}var exports={};importScripts("UglifyJS/parse-js.js");var parse=exports;var exports={};importScripts("UglifyJS/process.js");var process=exports;}catch(e){} function formatted2() { var variable2 = 0; }`); assert.strictEqual(formattedCode, `function formatted1() { var variable1 = 0; } function withComments() { // comment return "functionWithComments"; } try { onmessage = function(event) { var source = event.data; var formattedSource = beautify(source); var mapping = buildMapping(source, formattedSource); postMessage({ formattedSource: formattedSource, mapping: mapping }) } ; function beautify(source) { var ast = parse.parse(source); var beautifyOptions = { indent_level: 4, indent_start: 0, quote_keys: false, space_colon: false }; return process.gen_code(ast, beautifyOptions) } function buildMapping(source, formattedSource) { var mapping = { original: [], formatted: [] }; var lastPosition = 0; var regexp = /(^|[^\\])\b((?=D)[$.w]+)\b/g; while (true) { var match = regexp.exec(formattedSource); if (!match) break; var position = source.indexOf(match[2], lastPosition); if (position === -1) throw "No match found in original source for " + match[2]; mapping.original.push(position); mapping.formatted.push(match.index + match[1].length); lastPosition = position + match[2].length } return mapping } function require() { return parse } var exports = {}; importScripts("UglifyJS/parse-js.js"); var parse = exports; var exports = {}; importScripts("UglifyJS/process.js"); var process = exports; } catch (e) {} function formatted2() { var variable2 = 0; } `); }); it('formats functions using `import.meta.url` correctly', () => { const formattedCode = formatJavaScript('function foo(){console.log(import.meta.url);}'); assert.strictEqual(formattedCode, `function foo() { console.log(import.meta.url); } `); }); it('formats class fields correctly', () => { const formattedCode = formatJavaScript('class Clazz {map=new Map();someMethod(){console.log(42);}map2=new Map();}'); assert.strictEqual(formattedCode, `class Clazz { map = new Map(); someMethod() { console.log(42); } map2 = new Map(); } `); }); it('formats template literals correctly', () => { const formattedCode = formatJavaScript('`foo${bar}foo${bar}`'); assert.strictEqual(formattedCode, '`foo${bar}foo${bar}`\n'); }); it('formats template literals with nested JS expressions correctly', () => { const formattedCode = formatJavaScript('`${function(){let a}}`'); assert.strictEqual( formattedCode, '`${function() {\n' + ' let a\n' + '}\n' + '}`\n'); }); it('formats methods on literals correctly', () => { const formattedCode = formatJavaScript('num=1 .toString();str="abc" . toUpperCase();'); assert.strictEqual(formattedCode, `num = 1 .toString(); str = "abc".toUpperCase(); `); }); });