Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add transform callback to options so that tooling authors can customize the transform and still have correct parse / process results without a bunch of index math / conversions #75

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
import {Preprocessor} from 'content-tag';

const p = new Preprocessor();
const output = p.process('<template>Hi from createPreprocessor</template>');
const output = p.process(`
<template>
Hello World, from Vite!
</template>
`, {
transform: (src, type) => {
return `hi: ${type} src: ${src}`;
}
});

document.querySelector('#output').innerHTML = output;
</script>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

11 changes: 11 additions & 0 deletions src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,28 @@ impl Options {
None
};

let option_transform = Reflect::get(&options, &"transform".into()).unwrap();
// NOTE: That this will be None if this value was passed but not as a valid transform
// We probably want to error here.
let transform = option_transform.dyn_into::<js_sys::Function>().ok();
// transform
// .call1(&JsValue::NULL, &JsValue::from("hi"))
// .unwrap();

Self {
// unwrap is justified here for the same reasons as commented above
inline_source_map: js_boolean(
&Reflect::get(&options, &"inline_source_map".into()).unwrap(),
),
filename,
transform,
}
} else {
Self {
inline_source_map: false,
filename: None,
// transform: Default?
transform: None,
}
}
}
Expand Down
14 changes: 6 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use base64::{engine::general_purpose, Engine as _};
use std::path::PathBuf;
use swc_common::comments::SingleThreadedComments;
use swc_common::source_map::SourceMapGenConfig;
use swc_common::{self, sync::Lrc, FileName, SourceMap, Mark};
use swc_common::{self, sync::Lrc, FileName, Mark, SourceMap};
use swc_core::common::GLOBALS;
use swc_ecma_ast::{
Ident, ImportDecl, ImportNamedSpecifier, ImportSpecifier, Module, ModuleDecl,
ModuleExportName, ModuleItem,
Ident, ImportDecl, ImportNamedSpecifier, ImportSpecifier, Module, ModuleDecl, ModuleExportName,
ModuleItem,
};
use swc_ecma_codegen::Emitter;
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig};
Expand All @@ -21,14 +21,15 @@ use swc_ecma_utils::private_ident;
use swc_ecma_visit::{as_folder, VisitMutWith, VisitWith};

mod bindings;
mod locate;
mod snippets;
mod transform;
mod locate;

#[derive(Default)]
pub struct Options {
pub filename: Option<PathBuf>,
pub inline_source_map: bool,
pub transform: Option<js_sys::Function>,
}

pub struct Preprocessor {
Expand All @@ -47,7 +48,6 @@ impl SourceMapGenConfig for SourceMapConfig {
}
}


impl Preprocessor {
pub fn new() -> Self {
Self {
Expand Down Expand Up @@ -123,6 +123,7 @@ impl Preprocessor {
parsed_module.visit_mut_with(&mut as_folder(transform::TransformVisitor::new(
&id,
Some(&mut needs_import),
options.transform,
)));

if !had_id_already && needs_import {
Expand Down Expand Up @@ -281,12 +282,9 @@ fn simplify_imports(parsed_module: &mut Module) {
}
}



#[cfg(test)]
mod test_helpers;


macro_rules! testcase {
($test_name:ident, $input:expr, $expected:expr) => {
#[test]
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fn main() {
Options {
filename: Some(filename),
inline_source_map: true,
transform: None,
},
);

Expand Down
39 changes: 37 additions & 2 deletions src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ use swc_atoms::Atom;
pub struct TransformVisitor<'a> {
template_identifier: Ident,
found_it: Option<&'a mut bool>,
transform: Option<js_sys::Function>,
}

impl<'a> TransformVisitor<'a> {
pub fn new(id: &Ident, found_it: Option<&'a mut bool>) -> Self {
pub fn new(
id: &Ident,
found_it: Option<&'a mut bool>,
transform: Option<js_sys::Function>,
) -> Self {
TransformVisitor {
template_identifier: id.clone(),
found_it,
transform,
}
}
fn set_found_it(&mut self) {
Expand All @@ -40,6 +46,22 @@ impl<'a> TransformVisitor<'a> {
..
} = expr;

// Replace the entirety of the <template>...</template> expression
// with the results of the transform call
if let Some(transform) = self.transform.as_ref() {
let content = transform
.call2(
&wasm_bindgen::JsValue::NULL,
&wasm_bindgen::JsValue::from(&contents.value.to_string()),
&wasm_bindgen::JsValue::from("expression"),
/* utils ( for bindImport )*/
)
.unwrap();

// TODO: error if content is not a string
return Expr::Lit(content.as_string().unwrap().into());
}

Expr::Call(CallExpr {
span: *span,
callee: Callee::Expr(Box::new(Expr::Ident(self.template_identifier.clone()))),
Expand Down Expand Up @@ -67,7 +89,11 @@ impl<'a> TransformVisitor<'a> {
}

fn escape_template_literal(input: &Atom) -> Atom {
input.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$").into()
input
.replace("\\", "\\\\")
.replace("`", "\\`")
.replace("$", "\\$")
.into()
}

impl<'a> VisitMut for TransformVisitor<'a> {
Expand Down Expand Up @@ -154,6 +180,7 @@ test!(
as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
))
},
content_tag_template_expression,
Expand All @@ -166,6 +193,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
content_tag_template_member,
r#"class X { <template>Hello</template> } "#,
Expand All @@ -181,6 +209,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
expression_inside_class_member,
r#"class X { thing = <template>Hello</template> } "#,
Expand All @@ -194,6 +223,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
class_member_inside_expression,
r#"let x = class { <template>Hello</template> } "#,
Expand All @@ -209,6 +239,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
content_tag_export_default,
r#"<template>Hello</template>"#,
Expand All @@ -220,6 +251,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
inner_expression,
r#"let x = doIt(<template>Hello</template>)"#,
Expand All @@ -231,6 +263,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
backtick_in_template,
r#"let x = <template>He`llo</template>"#,
Expand All @@ -242,6 +275,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
dollar_in_template,
r#"let x = <template>He${ll}o</template>"#,
Expand All @@ -253,6 +287,7 @@ test!(
|_| as_folder(TransformVisitor::new(
&Ident::new("template".into(), Default::default()),
None,
None,
)),
do_not_interpret_js_escapes_in_hbs,
r#"let x = <template>Hello\nWorld\u1234</template>"#,
Expand Down
2 changes: 1 addition & 1 deletion test/parse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe(`parse`, function () {
p.process(
`const thing = "face";
<template>Hi`,
{ filename: 'path/to/my/component.gjs' }
{ filename: "path/to/my/component.gjs" }
);
}).to.throw(`Parse Error at path/to/my/component.gjs:2:15: 2:15`);
});
Expand Down
33 changes: 32 additions & 1 deletion test/process.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,35 @@ const { expect } = chai;
const p = new Preprocessor();

describe(`process`, function () {
it("tmp", function () {
let output = p.process("<template>Hi</template>", {
transform: (src, type, utils) => {
console.log("Did it run?");
let t = "todo";
// let t = utils.bindImport("@ember/template-compiler", "template");

if (type === "expression") {
return `${t}("${utils.escapeStringLiteral(
src
)}", { eval() { console.log("hi"); return eval(arguments[0]) } }`;
} else {
return `static {
${t}("${utils.escapeStringLiteral(src)}", {
eval() { return eval(arguments[0]) },
component: this,
})
}`;
}
},
});

expect(output).to
.equalCode(`import { template } from "@ember/template-compiler";
export default template(\`Hi\`, {
eval () { console.log("hi"); return eval(arguments[0]) }
});`);
});

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

Expand Down Expand Up @@ -91,7 +120,9 @@ describe(`process`, function () {
});

it("Provides inline source maps if inline_source_map option is set to true", function () {
let output = p.process(`<template>Hi</template>`, { inline_source_map: true });
let output = p.process(`<template>Hi</template>`, {
inline_source_map: true,
});

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