Skip to content

Commit

Permalink
feat: Normalize quotes around string literals (#59)
Browse files Browse the repository at this point in the history
* feat: Modify RepresenterRewriteOutput to use quotes around string literals

This commit modifies the RepresenterRewriteOutput class to ensure that string literals are enclosed in backticks (`) when generating the representation.

---------

Co-authored-by: Erik Schierboom <[email protected]>
  • Loading branch information
Cool-Katt and ErikSchierboom authored Nov 17, 2023
1 parent 78eb07c commit 97a9968
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 9 deletions.
25 changes: 23 additions & 2 deletions src/output/RepresenterRewriteOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
simpleTraverse,
} from '@typescript-eslint/typescript-estree'
import type { ExecutionOptions, Output } from '../interface'
import { generate } from 'astring'
import { generate, GENERATOR } from 'astring'
import type { State } from 'astring'
import { getProcessLogger } from '@exercism/static-analysis'
import {
RESERVED_NAMES,
Expand All @@ -18,6 +19,11 @@ import {

type Program = TSESTree.Program
type Node = TSESTree.Node
type LiteralNode = {
type: string | null
value: string | number | boolean | null
raw: string
}

export class RepresenterRewriteOutput implements Output {
constructor(public readonly representation: Program) {}
Expand All @@ -29,7 +35,22 @@ export class RepresenterRewriteOutput implements Output {
getProcessLogger().log(JSON.stringify(this.representation, undefined, 2))
const normalized = normalizeRepresentation(this.representation)

const representation = generate(normalized.representation, {})
const quotesFormatGenerator = Object.assign({}, GENERATOR, {
Literal: function (node: LiteralNode, state: State) {
const { type, value, raw } = node
const quote = '`'
if (type === 'Literal' && typeof value === 'string') {
state.write(`${quote}${value}${quote}`)
} else {
// passthrough for literals that are not strings -> numbers, bools and null
state.write(raw)
}
},
})

const representation = generate(normalized.representation, {
generator: quotesFormatGenerator,
})
const mapping = JSON.stringify(normalized.mapping, undefined, spaces)

return Promise.resolve({ representation, mapping })
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/clock/pass/expected_representation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ const IDENTIFIER_1 = 24;
const IDENTIFIER_2 = IDENTIFIER_0 * IDENTIFIER_1;
function IDENTIFIER_3(IDENTIFIER_4) {
while (IDENTIFIER_4 < 0) {
IDENTIFIER_5.IDENTIFIER_6("minutes < 0", IDENTIFIER_4);
IDENTIFIER_5.IDENTIFIER_6(`minutes < 0`, IDENTIFIER_4);
IDENTIFIER_4 += IDENTIFIER_2;
}
return IDENTIFIER_4 % IDENTIFIER_2;
}
export const clockPad = IDENTIFIER_8 => {
return String(IDENTIFIER_8).IDENTIFIER_9(2, '0');
return String(IDENTIFIER_8).IDENTIFIER_9(2, `0`);
};
export class Clock {
constructor(IDENTIFIER_11, IDENTIFIER_4 = 0) {
Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/general/normalize-quotes/expected_mapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"IDENTIFIER_1": "colourDuo",
"IDENTIFIER_2": "checkNulls",
"IDENTIFIER_3": "checkNumbers",
"IDENTIFIER_4": "checkString",
"IDENTIFIER_5": "checkBool",
"IDENTIFIER_6": "checkNesting",
"IDENTIFIER_7": "first",
"IDENTIFIER_8": "second",
"IDENTIFIER_9": "rest",
"IDENTIFIER_10": "COLORS",
"IDENTIFIER_11": "indexOf"
}
10 changes: 10 additions & 0 deletions test/fixtures/general/normalize-quotes/expected_representation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const decodedValue = IDENTIFIER_1 => {
let IDENTIFIER_2 = [null, null, null];
let IDENTIFIER_3 = 666;
let IDENTIFIER_4 = `please work`;
let IDENTIFIER_5 = true;
let IDENTIFIER_6 = `this string has "double" and escaped 'single'`;
let [IDENTIFIER_7, IDENTIFIER_8, ...IDENTIFIER_9] = IDENTIFIER_1;
return Number(IDENTIFIER_10.IDENTIFIER_11(IDENTIFIER_7) + `` + IDENTIFIER_10.IDENTIFIER_11(IDENTIFIER_8));
};
const IDENTIFIER_10 = [`black`, `brown`, `red`, `orange`, `yellow`, `green`, `blue`, `violet`, `grey`, `white`];
30 changes: 30 additions & 0 deletions test/fixtures/general/normalize-quotes/resistor-color-duo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// This is only a SKELETON file for the 'Resistor Color Duo' exercise. It's been provided as a
// convenience to get you started writing code faster.
//

export const decodedValue = (colourDuo) => {
let checkNulls = [null, null, null]
let checkNumbers = 666
let checkString = "please work"
let checkBool = true
let checkNesting = 'this string has "double" and escaped \'single\''
let [first, second, ...rest] = colourDuo
return Number(COLORS.indexOf(first) +''+ COLORS.indexOf(second))
};

// Here is a comment that contains 'single' and "double" quotes
// And here is another one with `backticks`

const COLORS = [
'black',
"brown",
`red`,
"orange",
'yellow',
"green",
'blue',
'violet',
'grey',
'white',
]
23 changes: 23 additions & 0 deletions test/fixtures/general/normalize-quotes/resistor-color-duo.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { decodedValue } from './resistor-color-duo';

describe('Resistor Colors', () => {
test('Brown and black', () => {
expect(decodedValue(['brown', 'black'])).toEqual(10);
});

xtest('Blue and grey', () => {
expect(decodedValue(['blue', 'grey'])).toEqual(68);
});

xtest('Yellow and violet', () => {
expect(decodedValue(['yellow', 'violet'])).toEqual(47);
});

xtest('Orange and orange', () => {
expect(decodedValue(['orange', 'orange'])).toEqual(33);
});

xtest('Ignore additional colors', () => {
expect(decodedValue(['green', 'brown', 'orange'])).toEqual(51);
});
});
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const twoFer = (IDENTIFIER_1 = 'you') => `One for ${IDENTIFIER_1}, one for me.`;
export const twoFer = (IDENTIFIER_1 = `you`) => `One for ${IDENTIFIER_1}, one for me.`;
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const twoFer = (IDENTIFIER_1 = 'you') => `One for ${IDENTIFIER_1}, one for me.`;
export const twoFer = (IDENTIFIER_1 = `you`) => `One for ${IDENTIFIER_1}, one for me.`;
4 changes: 2 additions & 2 deletions test/fixtures/two-fer/fail/expected_representation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export const twoFer = IDENTIFIER_1 => {
IDENTIFIER_2.IDENTIFIER_3(IDENTIFIER_1);
return `One for you, one for me.`;
};
IDENTIFIER_2.IDENTIFIER_3("Hello there debug message");
IDENTIFIER_2.IDENTIFIER_4("Ok there log message");
IDENTIFIER_2.IDENTIFIER_3(`Hello there debug message`);
IDENTIFIER_2.IDENTIFIER_4(`Ok there log message`);
2 changes: 1 addition & 1 deletion test/fixtures/two-fer/pass/expected_representation.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const twoFer = (IDENTIFIER_1 = 'you') => `One for ${IDENTIFIER_1}, one for me.`;
export const twoFer = (IDENTIFIER_1 = `you`) => `One for ${IDENTIFIER_1}, one for me.`;

0 comments on commit 97a9968

Please sign in to comment.