Skip to content

Commit ec44c93

Browse files
author
Robert Jackson
committed
Add codemod mode to support <pre>\nhi</pre> stability in codemods.
There have been a few different takes on a fix here, but at the moment code like this: ```hbs <textarea> some content </textarea> ``` Will always be parsed as if you had written `<textarea>some content</textarea>`. This is perfectly reasonable when the goal is to precompile templates for actual rendering (e.g. what Ember does) but it completely breaks the ability of codemod utilities to properly rewrite / update in a source <-> source scenario. After this change, `@glimmer/syntax` will be able to pass through its own `mode` (which matches the type added here) and _already_ opts folks in to this mental model. That `mode` is used by tools like Prettier and `ember-template-recast` already.
1 parent 5a77397 commit ec44c93

4 files changed

Lines changed: 28 additions & 3 deletions

File tree

src/evented-tokenizer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export default class EventedTokenizer {
1414

1515
constructor(
1616
private delegate: TokenizerDelegate,
17-
private entityParser: EntityParser
17+
private entityParser: EntityParser,
18+
private mode: 'codemod' | 'precompile' = 'precompile'
1819
) {
1920
this.reset();
2021
}
@@ -131,7 +132,7 @@ export default class EventedTokenizer {
131132
this.markTagStart();
132133
this.consume();
133134
} else {
134-
if (char === '\n') {
135+
if (this.mode === 'precompile' && char === '\n') {
135136
let tag = this.tagNameBuffer.toLowerCase();
136137

137138
if (tag === 'pre' || tag === 'textarea') {

src/tokenizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default class Tokenizer implements TokenizerDelegate {
2121
entityParser: EntityParser,
2222
private options: TokenizerOptions = {}
2323
) {
24-
this.tokenizer = new EventedTokenizer(this, entityParser);
24+
this.tokenizer = new EventedTokenizer(this, entityParser, options.mode);
2525
this._currentAttribute = undefined;
2626
}
2727

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface EntityParser {
88

99
export interface TokenizerOptions {
1010
loc?: boolean;
11+
mode?: 'precompile' | 'codemod';
1112
}
1213

1314
export type Attribute = [string, string, boolean];

tests/tokenizer-tests.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,29 @@ QUnit.test('A newline immediately following a <textarea> tag is stripped', funct
222222
assert.deepEqual(tokens, [startTag('textarea'), chars('hello'), endTag('textarea')]);
223223
});
224224

225+
// https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
226+
QUnit.test('codemod: A newline immediately following a <pre> tag is stripped', function(assert) {
227+
let tokens = tokenize("<pre>\nhello</pre>", { mode: 'codemod' });
228+
assert.deepEqual(tokens, [startTag('pre'), chars('\nhello'), endTag('pre')]);
229+
});
230+
231+
QUnit.test('codemod: A newline immediately following a closing </pre> tag is not stripped', function(assert) {
232+
let tokens = tokenize("\n<pre>\nhello</pre>\n", { mode: 'codemod' });
233+
assert.deepEqual(tokens, [chars('\n'), startTag('pre'), chars('\nhello'), endTag('pre'), chars('\n')]);
234+
});
235+
236+
// https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
237+
QUnit.test('codemod: A newline immediately following a <PRE> tag is stripped', function(assert) {
238+
let tokens = tokenize("<PRE>\nhello</PRE>", { mode: 'codemod' });
239+
assert.deepEqual(tokens, [startTag('PRE'), chars('\nhello'), endTag('PRE')]);
240+
});
241+
242+
// https://html.spec.whatwg.org/multipage/syntax.html#element-restrictions
243+
QUnit.test('codemod: A newline immediately following a <textarea> tag is stripped', function(assert) {
244+
let tokens = tokenize("<textarea>\nhello</textarea>", { mode: 'codemod' });
245+
assert.deepEqual(tokens, [startTag('textarea'), chars('\nhello'), endTag('textarea')]);
246+
});
247+
225248
// https://html.spec.whatwg.org/multipage/semantics.html#the-title-element
226249
QUnit.test('The title element content is always text', function(assert) {
227250
let tokens = tokenize("<title>&quot;hey <b>there</b><!-- comment --></title>");

0 commit comments

Comments
 (0)