Skip to content

Commit

Permalink
Merge pull request #83 from wagenet/strip-hygiene-code
Browse files Browse the repository at this point in the history
Strip hygiene code in favor of UUIDs
  • Loading branch information
ef4 authored Nov 5, 2024
2 parents 46eb095 + 8d8cb16 commit 578867f
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 118 deletions.
28 changes: 17 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,16 @@ serde-wasm-bindgen = "0.4"
wasm-bindgen = "0.2.95"
js-sys = "0.3.64"

[dependencies.uuid]
version = "1.11.0"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng" # Use a faster (but still sufficiently random) RNG
]

[dev-dependencies]
difference = "2"
regex = "1.11.1"



127 changes: 33 additions & 94 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use swc_ecma_ast::{
};
use swc_ecma_codegen::Emitter;
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_transforms::hygiene::hygiene_with_config;
use swc_ecma_transforms::resolver;
use swc_ecma_utils::private_ident;
use swc_ecma_visit::{as_folder, VisitMutWith, VisitWith};
use uuid::Uuid;

mod bindings;
mod snippets;
Expand Down Expand Up @@ -116,16 +116,14 @@ impl Preprocessor {
GLOBALS.set(&Default::default(), || {
let mut parsed_module = parser.parse_module()?;

let found_id = find_existing_import(&parsed_module, target_module, target_specifier);
let had_id_already = found_id.is_some();
let id = found_id.unwrap_or_else(|| private_ident!(target_specifier));
let id = private_ident!(format!("{}_{}", target_specifier, Uuid::new_v4().to_string().replace("-", "")));
let mut needs_import = false;
parsed_module.visit_mut_with(&mut as_folder(transform::TransformVisitor::new(
&id,
Some(&mut needs_import),
)));

if !had_id_already && needs_import {
if needs_import {
insert_import(&mut parsed_module, target_module, target_specifier, &id)
}

Expand All @@ -134,16 +132,6 @@ impl Preprocessor {

parsed_module.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false));

let mut h = hygiene_with_config(swc_ecma_transforms::hygiene::Config {
keep_class_names: true,
top_level_mark,
safari_10: false,
ignore_eval: false,
});
parsed_module.visit_mut_with(&mut h);

simplify_imports(&mut parsed_module);

Ok(self.print(&parsed_module, options.inline_source_map))
})
}
Expand Down Expand Up @@ -192,38 +180,6 @@ impl Preprocessor {
}
}

fn find_existing_import(
parsed_module: &Module,
target_module: &str,
target_specifier: &str,
) -> Option<Ident> {
for item in parsed_module.body.iter() {
match item {
ModuleItem::ModuleDecl(ModuleDecl::Import(import_declaration)) => {
if import_declaration.src.value.to_string() == target_module {
for specifier in import_declaration.specifiers.iter() {
match specifier {
ImportSpecifier::Named(s) => {
let imported = match &s.imported {
Some(ModuleExportName::Ident(i)) => i.sym.to_string(),
Some(ModuleExportName::Str(s)) => s.value.to_string(),
None => s.local.sym.to_string(),
};
if imported == target_specifier {
return Some(s.local.clone());
}
}
_ => {}
}
}
}
}
_ => {}
}
}
None
}

fn insert_import(
parsed_module: &mut Module,
target_module: &str,
Expand All @@ -250,39 +206,6 @@ fn insert_import(
);
}

// It's not until after the hygiene pass that we know what local name is being
// used for our import. If it turns out to equal the imported name, we can
// implify from "import { template as template } from..." down to "import {
// template } from ...".
fn simplify_imports(parsed_module: &mut Module) {
for item in parsed_module.body.iter_mut() {
match item {
ModuleItem::ModuleDecl(ModuleDecl::Import(import_declaration)) => {
for specifier in import_declaration.specifiers.iter_mut() {
match specifier {
ImportSpecifier::Named(specifier) => {
if let ImportNamedSpecifier {
imported: Some(ModuleExportName::Ident(imported)),
local,
..
} = specifier
{
if local.sym == imported.sym {
specifier.imported = None;
}
}
}
_ => {}
}
}
}
_ => {}
}
}
}



#[cfg(test)]
mod test_helpers;

Expand All @@ -299,24 +222,26 @@ macro_rules! testcase {
testcase! {
no_preexisting_import,
r#"let x = <template>hello</template>"#,
r#"import { template } from "@ember/template-compiler";
let x = template(`hello`, { eval() { return eval(arguments[0])} });"#
r#"import { template as template_UUID } from "@ember/template-compiler";
let x = template_UUID(`hello`, { eval() { return eval(arguments[0])} });"#
}

testcase! {
uses_preexisting_import,
preexisting_import,
r#"import { template } from "@ember/template-compiler";
let x = <template>hello</template>"#,
r#"import { template } from "@ember/template-compiler";
let x = template(`hello`, { eval() { return eval(arguments[0])} });"#
r#"import { template as template_UUID } from "@ember/template-compiler";
import { template } from "@ember/template-compiler";
let x = template_UUID(`hello`, { eval() { return eval(arguments[0])} });"#
}

testcase! {
uses_preexisting_renamed_import,
preexisting_renamed_import,
r#"import { template as t } from "@ember/template-compiler";
let x = <template>hello</template>"#,
r#"import { template as t } from "@ember/template-compiler";
let x = t(`hello`, { eval() { return eval(arguments[0])} })"#
r#"import { template as template_UUID } from "@ember/template-compiler";
import { template as t } from "@ember/template-compiler";
let x = template_UUID(`hello`, { eval() { return eval(arguments[0])} })"#
}

testcase! {
Expand All @@ -330,10 +255,10 @@ testcase! {
r#"function template() {};
console.log(template());
export default <template>Hi</template>"#,
r#"import { template as template1 } from "@ember/template-compiler";
r#"import { template as template_UUID } from "@ember/template-compiler";
function template() {};
console.log(template());
export default template1(`Hi`, { eval() { return eval(arguments[0])} });"#
export default template_UUID(`Hi`, { eval() { return eval(arguments[0])} });"#
}

testcase! {
Expand All @@ -342,10 +267,10 @@ testcase! {
console.log(template);
return <template>X</template>;
};"#,
r#"import { template as template1 } from "@ember/template-compiler";
r#"import { template as template_UUID } from "@ember/template-compiler";
export default function(template) {
console.log(template);
return template1(`X`, { eval() { return eval(arguments[0])} });
return template_UUID(`X`, { eval() { return eval(arguments[0])} });
};"#
}

Expand All @@ -355,9 +280,23 @@ testcase! {
console.log(message);
return <template>hello</template>
}"#,
r#"import { template } from "@ember/template-compiler";
r#"import { template as template_UUID } from "@ember/template-compiler";
function makeComponent(message: string) {
console.log(message);
return template(`hello`, { eval() { return eval(arguments[0]) } });
return template_UUID(`hello`, { eval() { return eval(arguments[0]) } });
}"#
}

testcase! {
handles_typescript_this,
r#"function f(this: Context, ...args: unknown[]) {
function t(this: Context, ...args: unknown[]) {};
return <template></template>
}"#,
r#"import { template as template_UUID } from "@ember/template-compiler";
function f(this: Context, ...args: unknown[]) {
function t(this: Context, ...args: unknown[]) {}
;
return template_UUID(``, { eval() { return eval(arguments[0]) } });
}"#
}
7 changes: 5 additions & 2 deletions src/test_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use difference::Changeset;
use regex::Regex;
use swc_common::comments::SingleThreadedComments;
use swc_common::{self, sync::Lrc, FileName, SourceMap};
use swc_ecma_codegen::Emitter;
Expand All @@ -7,13 +8,15 @@ use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
use crate::Preprocessor;

pub fn testcase(input: &str, expected: &str) -> Result<(), swc_ecma_parser::error::Error> {
let re = Regex::new(r"template_[0-9a-f]{32}").unwrap();
let p = Preprocessor::new();
let actual = p.process(input, Default::default())?;
let actual_santized = re.replace_all(&actual, "template_UUID");
let normalized_expected = normalize(expected);
if actual != normalized_expected {
if actual_santized != normalized_expected {
panic!(
"code differs from expected:\n{}",
format!("{}", Changeset::new(&actual, &normalized_expected, "\n"))
format!("{}", Changeset::new(&actual_santized, &normalized_expected, "\n"))
);
}
Ok(())
Expand Down
20 changes: 12 additions & 8 deletions test/process.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ const { expect } = chai;

const p = new Preprocessor();

function normalizeOutput(output) {
return output.replace(/template_[0-9a-f]{32}/g, "template_UUID");
}

describe(`process`, function () {
it("works for a basic example", function () {
let output = p.process("<template>Hi</template>");

expect(output).to
.equalCode(`import { template } from "@ember/template-compiler";
export default template(\`Hi\`, {
expect(normalizeOutput(output)).to
.equalCode(`import { template as template_UUID } from "@ember/template-compiler";
export default template_UUID(\`Hi\`, {
eval () {
return eval(arguments[0]);
}
Expand All @@ -32,12 +36,12 @@ describe(`process`, function () {

let output = p.process(input);

expect(output).to.equalCode(
`import { template } from "@ember/template-compiler";
let Foo = class Foo extends Component {
expect(normalizeOutput(output)).to.equalCode(
`import { template as template_UUID } from "@ember/template-compiler";
class Foo extends Component {
greeting = 'Hello';
static{
template(\`{{this.greeting}}, \\\`lifeform\\\`!\`, {
template_UUID(\`{{this.greeting}}, \\\`lifeform\\\`!\`, {
component: this,
eval () {
return eval(arguments[0]);
Expand Down Expand Up @@ -94,7 +98,7 @@ describe(`process`, function () {
let output = p.process(`<template>Hi</template>`, { inline_source_map: true });

expect(output).to.match(
/sourceMappingURL=data:application\/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyI8dGVtcGxhdGU-SGk8L3RlbXBsYXRlPiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsZUFBQSxTQUFVLENBQUEsRUFBRSxDQUFBLEVBQUE7SUFBQTtRQUFBLE9BQUEsS0FBQSxTQUFBLENBQUEsRUFBVztJQUFEO0FBQUEsR0FBQyJ9/
/sourceMappingURL=data:application\/json;base64,/
);
});
});
Loading

0 comments on commit 578867f

Please sign in to comment.