Skip to content

Commit

Permalink
Fix bug where preceding {} was slurping up template
Browse files Browse the repository at this point in the history
  • Loading branch information
gitKrystan committed Dec 29, 2023
1 parent f95f948 commit de7f183
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 112 deletions.
32 changes: 21 additions & 11 deletions src/parse/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { traverse } from '@babel/core';
import type { Node, ObjectExpression, StaticBlock } from '@babel/types';
import type {
BlockStatement,
Node,
ObjectExpression,
StaticBlock,
} from '@babel/types';
import type { Parsed as RawGlimmerTemplate } from 'content-tag';
import { Preprocessor } from 'content-tag';
import type { Parser } from 'prettier';
Expand All @@ -15,7 +20,7 @@ const p = new Preprocessor();

/** Converts a node into a GlimmerTemplate node */
function convertNode(
node: ObjectExpression | StaticBlock,
node: BlockStatement | ObjectExpression | StaticBlock,
rawTemplate: RawGlimmerTemplate,
): void {
node.extra = Object.assign(node.extra ?? {}, {
Expand All @@ -31,27 +36,32 @@ function convertAst(ast: Node, rawTemplates: RawGlimmerTemplate[]): void {
traverse(ast, {
enter(path) {
const { node } = path;
if (node.type === 'ObjectExpression' || node.type === 'StaticBlock') {
if (
node.type === 'BlockStatement' ||
node.type === 'ObjectExpression' ||
node.type === 'StaticBlock'
) {
const { range } = node;
assert('expected range', range);
const [start, end] = range;

const templateIndex = unprocessedTemplates.findIndex(
(t) =>
(node.type === 'StaticBlock' &&
t.range.start === start &&
t.range.end === end) ||
(t.range.start === start && t.range.end === end) ||
(node.type === 'ObjectExpression' &&
node.extra?.['parenthesized'] === true &&
t.range.start === start - 1 &&
t.range.end === end + 1),
);
const rawTemplate = unprocessedTemplates.splice(templateIndex, 1)[0];

if (!rawTemplate) {
if (templateIndex > -1) {
const rawTemplate = unprocessedTemplates.splice(templateIndex, 1)[0];
if (!rawTemplate) {
return null;
}
convertNode(node, rawTemplate);
} else {
return null;
}

convertNode(node, rawTemplate);
}

return null;
Expand Down
30 changes: 22 additions & 8 deletions src/parse/preprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ const EMPTY_SPACE = ' ';
* Returns the resulting string.
*/
function replaceByteRange(
original: string,
originalBuffer: Buffer,
range: { start: number; end: number },
options: { prefix: string; suffix: string },
): string {
// Convert the original string and the prefix, suffix to buffers for byte manipulation
const originalBuffer = Buffer.from(original);
const prefixBuffer = Buffer.from(options.prefix);
const suffixBuffer = Buffer.from(options.suffix);

Expand All @@ -27,7 +25,11 @@ function replaceByteRange(
prefixBuffer.length + suffixBuffer.length > range.end - range.start
) {
throw new Error(
`Invalid byte range:\n\tstart=${range.start}\n\tend=${range.end}\n\tprefix=${options.prefix}\n\tsuffix=${options.suffix}\n\tstring=\n\t${original}`,
`Invalid byte range:\n\tstart=${range.start}\n\tend=${
range.end
}\n\tprefix=${options.prefix}\n\tsuffix=${
options.suffix
}\n\tstring=\n\t${originalBuffer.toString()}`,
);
}

Expand Down Expand Up @@ -70,6 +72,8 @@ export function preprocessTemplateRange(
rawTemplate: RawGlimmerTemplate,
code: string,
): string {
const codeBuffer = Buffer.from(code);

let prefix: string;
let suffix: string;

Expand All @@ -78,12 +82,22 @@ export function preprocessTemplateRange(
prefix = 'static{';
suffix = '}';
} else {
// Replace with ObjectExpression
prefix = '({';
suffix = '})';
// Replace with BlockStatement or ObjectExpression
prefix = '{';
suffix = '}';

const nextToken = codeBuffer
.subarray(rawTemplate.range.end)
.toString()
.match(/\S+/);
if (nextToken && nextToken[0] === 'as') {
// Replace with parenthesized ObjectExpression
prefix = '(' + prefix;
suffix = suffix + ')';
}
}

return replaceByteRange(code, rawTemplate.range, {
return replaceByteRange(codeBuffer, rawTemplate.range, {
prefix,
suffix,
});
Expand Down
8 changes: 6 additions & 2 deletions src/print/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,14 @@ export const printer: Printer<Node | undefined> = {
embedOptions as Options,
);

return printTemplateTag(content);
const printed = printTemplateTag(content);
saveCurrentPrintOnSiblingNode(path, printed);
return printed;
} catch (error) {
console.error(error);
return printRawText(path, embedOptions as Options);
const printed = [printRawText(path, embedOptions as Options)];
saveCurrentPrintOnSiblingNode(path, printed);
return printed;
}
}

Expand Down
45 changes: 24 additions & 21 deletions src/types/glimmer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
BlockStatement,
ExportDefaultDeclaration,
ExpressionStatement,
Node,
Expand All @@ -8,7 +9,11 @@ import type {
} from '@babel/types';
import type { Parsed as RawGlimmerTemplate } from 'content-tag';

type GlimmerTemplate = (ObjectExpression | StaticBlock) & {
type GlimmerTemplateProperties = (
| BlockStatement
| ObjectExpression
| StaticBlock
) & {
/**
* Range of the contents, inclusive of inclusive of the
* `<template></template>` tags.
Expand All @@ -28,14 +33,17 @@ type GlimmerTemplate = (ObjectExpression | StaticBlock) & {
};
};

type GlimmerTemplate = (BlockStatement | ObjectExpression | StaticBlock) &
GlimmerTemplateProperties;

/** Returns true if the node is a GlimmerTemplate. */
export function isGlimmerTemplate(node: Node): node is Node & GlimmerTemplate {
return node.extra?.['isGlimmerTemplate'] === true;
}

export type GlimmerTemplateParent =
| GlimmerExpressionStatement
| GlimmerExpressionStatementTS
| GlimmerStatement
| GlimmerStatementTS
| GlimmerExportDefaultDeclaration
| GlimmerExportDefaultDeclarationTS;

Expand All @@ -50,16 +58,14 @@ export function isGlimmerTemplateParent(

return (
isGlimmerTemplate(node) ||
isGlimmerExpressionStatement(node) ||
isGlimmerExpressionStatementTS(node) ||
isGlimmerStatement(node) ||
isGlimmerStatementTS(node) ||
isGlimmerExportDefaultDeclaration(node) ||
isGlimmerExportDefaultDeclarationTS(node)
);
}

type GlimmerExpressionStatement = ExpressionStatement & {
expression: GlimmerTemplate;
};
type GlimmerStatement = BlockStatement & GlimmerTemplateProperties;

/**
* Type predicate for:
Expand All @@ -68,17 +74,13 @@ type GlimmerExpressionStatement = ExpressionStatement & {
* <template></template>;
* ```
*/
function isGlimmerExpressionStatement(
node: Node,
): node is GlimmerExpressionStatement {
return (
node.type === 'ExpressionStatement' && isGlimmerTemplate(node.expression)
);
function isGlimmerStatement(node: Node): node is GlimmerStatement {
return node.type === 'BlockStatement' && isGlimmerTemplate(node);
}

type GlimmerExpressionStatementTS = ExpressionStatement & {
type GlimmerStatementTS = ExpressionStatement & {
expression: TSAsExpression & {
expression: GlimmerTemplate;
expression: ObjectExpression & GlimmerTemplateProperties;
};
};

Expand All @@ -89,18 +91,17 @@ type GlimmerExpressionStatementTS = ExpressionStatement & {
* <template></template> as TemplateOnlyComponent<Signature>
* ```
*/
function isGlimmerExpressionStatementTS(
node: Node,
): node is GlimmerExpressionStatementTS {
function isGlimmerStatementTS(node: Node): node is GlimmerStatementTS {
return (
node.type === 'ExpressionStatement' &&
node.expression.type === 'TSAsExpression' &&
node.expression.expression.type === 'ObjectExpression' &&
isGlimmerTemplate(node.expression.expression)
);
}

type GlimmerExportDefaultDeclaration = ExportDefaultDeclaration & {
declaration: GlimmerTemplate;
declaration: ObjectExpression & GlimmerTemplateProperties;
};

/**
Expand All @@ -115,13 +116,14 @@ function isGlimmerExportDefaultDeclaration(
): node is GlimmerExportDefaultDeclaration {
return (
node.type === 'ExportDefaultDeclaration' &&
node.declaration.type === 'ObjectExpression' &&
isGlimmerTemplate(node.declaration)
);
}

type GlimmerExportDefaultDeclarationTS = ExportDefaultDeclaration & {
declaration: TSAsExpression & {
expression: GlimmerTemplate;
expression: ObjectExpression & GlimmerTemplateProperties;
};
};

Expand All @@ -138,6 +140,7 @@ function isGlimmerExportDefaultDeclarationTS(
return (
node.type === 'ExportDefaultDeclaration' &&
node.declaration.type === 'TSAsExpression' &&
node.declaration.expression.type === 'ObjectExpression' &&
isGlimmerTemplate(node.declaration.expression)
);
}
4 changes: 4 additions & 0 deletions tests/cases/gjs/preceded-by-object.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const foo = {}
<template>
hello
</template>
6 changes: 6 additions & 0 deletions tests/unit-tests/__snapshots__/format.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ exports[`format > config > default > it formats ../cases/gjs/one-line.gjs 1`] =
"
`;
exports[`format > config > default > it formats ../cases/gjs/preceded-by-object.gjs 1`] = `
"const foo = {};
<template>hello</template>
"
`;
exports[`format > config > default > it formats ../cases/gjs/prettier-ignore/component-class.gjs 1`] = `
"import Component from "@glimmer/component";
Expand Down
Loading

0 comments on commit de7f183

Please sign in to comment.