diff --git a/.gitignore b/.gitignore index 3c9aa04..687f983 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ meta.json rewriter/target rewriter/out static/wasm.js +rewriter/*.js +pnpm-lock.yaml diff --git a/farm.config.ts b/farm.config.ts new file mode 100644 index 0000000..715f762 --- /dev/null +++ b/farm.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "@farmfe/core"; +// import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin"; +import { join } from "path"; + +import sj from "scramjet-farm-plugin"; + +export default defineConfig({ + plugins: [sj()], + compilation: { + presetEnv: false, + mode: "development", + sourcemap: false, + input: { + worker: "src/worker/index.ts", + thread: "src/thread/thread.ts", + client: "src/client/index.ts", + config: "src/scramjet.config.ts", + }, + output: { + path: "dist", + format: "cjs", + targetEnv: "browser-esnext", + entryFilename: "scramjet.[entryName].js", + filename: "scramjet.split.[name].js", + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d3a889..baeabcf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,8 +52,8 @@ importers: specifier: ^2.1.3 version: 2.1.3(typescript@5.4.5) '@mercuryworkshop/libcurl-transport': - specifier: ^1.3.6 - version: 1.3.6(typescript@5.4.5) + specifier: ^1.3.7 + version: 1.3.7(typescript@5.4.5) '@rsdoctor/rspack-plugin': specifier: ^0.3.7 version: 0.3.7(@rspack/core@0.7.5)(bufferutil@4.0.8)(utf-8-validate@6.0.4) @@ -216,8 +216,8 @@ packages: '@mercuryworkshop/epoxy-transport@2.1.3': resolution: {integrity: sha512-cxuaPCVUmGnB1c/8cdoyhTIrZfL5feqH/sZRclkj8wFmrGz4PzzOHZRNMOrlzNoEiJDPYqHC4qJ6mNuiPC4vCw==} - '@mercuryworkshop/libcurl-transport@1.3.6': - resolution: {integrity: sha512-K4/TSfHlkjuzY6j0XXFmIbG8m3tpMoTxLga46fhmeuX8k3EJR/FVbW8JTXOD34Iofu0vCZuuY8n/ywNCcB6XjA==} + '@mercuryworkshop/libcurl-transport@1.3.7': + resolution: {integrity: sha512-Ef2BpZRPzkWaCf6FDv63uYyocz96sIwbScI2GrU/Oiu5Etyyq5bOo0mA6sHh8KCwioFSulvh61lO2MbfaEzH9g==} '@module-federation/runtime-tools@0.1.6': resolution: {integrity: sha512-7ILVnzMIa0Dlc0Blck5tVZG1tnk1MmLnuZpLOMpbdW+zl+N6wdMjjHMjEZFCUAJh2E5XJ3BREwfX8Ets0nIkLg==} @@ -1658,8 +1658,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libcurl.js@0.6.10: - resolution: {integrity: sha512-GDyxCRi7+R7XFhcCRFAsNYxg3x91LiwiBISo+pWfUXfgO45J/9ZWcHqjKQg8/FbWHRkV1wwSCyPOZ8pLlbdivg==} + libcurl.js@0.6.11: + resolution: {integrity: sha512-mCYCfjb5hnThNq0qzKPDUCRnHkDZENpCqGZ1flh4npu+zqWGun2/gsDmXCi+0J3TOsCDjmyMEt+W/eGNvqEHvQ==} light-my-request@5.13.0: resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} @@ -2741,10 +2741,10 @@ snapshots: transitivePeerDependencies: - typescript - '@mercuryworkshop/libcurl-transport@1.3.6(typescript@5.4.5)': + '@mercuryworkshop/libcurl-transport@1.3.7(typescript@5.4.5)': dependencies: esbuild-plugin-umd-wrapper: 2.0.0 - libcurl.js: 0.6.10 + libcurl.js: 0.6.11 rollup: 4.17.2 rollup-plugin-node-resolve: 5.2.0(rollup@4.17.2) rollup-plugin-typescript2: 0.36.0(rollup@4.17.2)(typescript@5.4.5) @@ -4441,7 +4441,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libcurl.js@0.6.10: {} + libcurl.js@0.6.11: {} light-my-request@5.13.0: dependencies: diff --git a/rewriter/src/rewrite.rs b/rewriter/src/rewrite.rs index cd6676d..b1a927f 100644 --- a/rewriter/src/rewrite.rs +++ b/rewriter/src/rewrite.rs @@ -9,7 +9,7 @@ use oxc_ast::{ }; use oxc_parser::Parser; use oxc_span::{SourceType, Span}; -use oxc_syntax::scope::ScopeFlags; +use oxc_syntax::{operator::AssignmentOperator, scope::ScopeFlags}; use url::Url; use urlencoding::encode; @@ -27,6 +27,7 @@ enum JsChange { name: String, entirespan: Span, rhsspan: Span, + op: AssignmentOperator, }, } @@ -122,6 +123,76 @@ impl<'a> Visit<'a> for Rewriter { walk::walk_object_expression(self, it); } + + fn visit_assignment_expression(&mut self, it: &oxc_ast::ast::AssignmentExpression<'a>) { + match &it.left { + AssignmentTarget::AssignmentTargetIdentifier(s) => { + if ["location"].contains(&s.name.to_string().as_str()) { + self.jschanges.push(JsChange::Assignment { + name: s.name.to_string(), + entirespan: it.span, + rhsspan: expression_span(&it.right), + op: it.operator, + }); + + // avoid walking rest of tree, i would need to figure out nested rewrites + // somehow + return; + } + } + _ => {} + } + walk::walk_assignment_expression(self, it); + } +} + +fn expression_span(e: &Expression) -> Span { + // enums.split("\n").filter(f=>f).map(p=>p.trimLeft()).filter(p=>!p.startsWith("#")).map(p=>p.replace(/\(.*/,"")).map(p=>`E::${p}(s) => s.span`).join(",\n") + use Expression as E; + match e { + E::BooleanLiteral(s) => s.span, + E::NullLiteral(s) => s.span, + E::NumericLiteral(s) => s.span, + E::BigIntLiteral(s) => s.span, + E::RegExpLiteral(s) => s.span, + E::StringLiteral(s) => s.span, + E::TemplateLiteral(s) => s.span, + E::Identifier(s) => s.span, + E::MetaProperty(s) => s.span, + E::Super(s) => s.span, + E::ArrayExpression(s) => s.span, + E::ArrowFunctionExpression(s) => s.span, + E::AssignmentExpression(s) => s.span, + E::AwaitExpression(s) => s.span, + E::BinaryExpression(s) => s.span, + E::CallExpression(s) => s.span, + E::ChainExpression(s) => s.span, + E::ClassExpression(s) => s.span, + E::ConditionalExpression(s) => s.span, + E::FunctionExpression(s) => s.span, + E::ImportExpression(s) => s.span, + E::LogicalExpression(s) => s.span, + E::NewExpression(s) => s.span, + E::ObjectExpression(s) => s.span, + E::ParenthesizedExpression(s) => s.span, + E::SequenceExpression(s) => s.span, + E::TaggedTemplateExpression(s) => s.span, + E::ThisExpression(s) => s.span, + E::UnaryExpression(s) => s.span, + E::UpdateExpression(s) => s.span, + E::YieldExpression(s) => s.span, + E::PrivateInExpression(s) => s.span, + E::JSXElement(s) => s.span, + E::JSXFragment(s) => s.span, + E::TSAsExpression(s) => s.span, + E::TSSatisfiesExpression(s) => s.span, + E::TSTypeAssertion(s) => s.span, + E::TSNonNullExpression(s) => s.span, + E::TSInstantiationExpression(s) => s.span, + E::ComputedMemberExpression(s) => s.span, + E::StaticMemberExpression(s) => s.span, + E::PrivateFieldExpression(s) => s.span, + } } // js MUST not be able to get a reference to any of these because sbx @@ -166,6 +237,7 @@ pub fn rewrite(js: &str, url: Url) -> Vec { name, entirespan, rhsspan, + op, } => entirespan.start, _ => 0, }; @@ -175,6 +247,7 @@ pub fn rewrite(js: &str, url: Url) -> Vec { name, entirespan, rhsspan, + op, } => entirespan.start, _ => 0, }; @@ -189,6 +262,12 @@ pub fn rewrite(js: &str, url: Url) -> Vec { JsChange::GenericChange { span, text } => { difference += text.len() as i32 - (span.end - span.start) as i32; } + JsChange::Assignment { + name, + entirespan, + rhsspan, + op, + } => difference += entirespan.size() as i32 + name.len() as i32 + 10, _ => {} } } @@ -200,7 +279,6 @@ pub fn rewrite(js: &str, url: Url) -> Vec { for change in ast_pass.jschanges { match &change { JsChange::GenericChange { span, text } => { - let len = (span.end - span.start) as usize; let start = span.start as usize; let end = span.end as usize; @@ -208,8 +286,47 @@ pub fn rewrite(js: &str, url: Url) -> Vec { buffer.extend_from_slice(text.as_bytes()); offset = end; + } + JsChange::Assignment { + name, + entirespan, + rhsspan, + op, + } => { + let start = entirespan.start as usize; + buffer.extend_from_slice(&js[offset..start].as_bytes()); - // offset = (offset as i64 + (text.len() as i64 - len as i64)) as usize; + let opstr = match op { + AssignmentOperator::Assign => "=", + AssignmentOperator::Addition => "+=", + AssignmentOperator::Subtraction => "-=", + AssignmentOperator::Multiplication => "*=", + AssignmentOperator::Division => "/=", + AssignmentOperator::Remainder => "%=", + AssignmentOperator::Exponential => "**=", + AssignmentOperator::ShiftLeft => "<<=", + AssignmentOperator::ShiftRight => ">>=", + AssignmentOperator::ShiftRightZeroFill => ">>>=", + AssignmentOperator::BitwiseAnd => "&=", + AssignmentOperator::BitwiseXOR => "^=", + AssignmentOperator::BitwiseOR => "|=", + AssignmentOperator::LogicalAnd => "&&=", + AssignmentOperator::LogicalOr => "||=", + AssignmentOperator::LogicalNullish => "??=", + }; + + buffer.extend_from_slice( + format!( + "((t)=>$tryset({},\"{}\",t)||{}=t)({})", + name, + opstr, + name, + &js[rhsspan.start as usize..rhsspan.end as usize] + ) + .as_bytes(), + ); + + offset = entirespan.end as usize; } _ => {} } diff --git a/rewriter/test.js b/rewriter/test.js index ad44f54..7edd13a 100644 --- a/rewriter/test.js +++ b/rewriter/test.js @@ -6,3 +6,22 @@ consle.log(globalThis["win" + "dow"]); globalThis.eval(".."); let ref = { b: this.top.window, c: globalThis["win" + "dow"] }; + + +export default ref; + +export { ref }; + + +export { ref as default }; + +export { S } from "module"; +export * from "module"; +import sd from "d" + +location += "http://example.com"; + +function f() { return import("x") } + + + diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..bd04ffa --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,39 @@ +import typescript from "rollup-plugin-typescript2"; +import { join } from "node:path"; +import fs from "node:fs"; +import { fileURLToPath } from "node:url"; + +import { nodeResolve } from "@rollup/plugin-node-resolve"; + +// check if its +const production = !process.env.ROLLUP_WATCH; +console.log(production); +fs.rmSync(join(fileURLToPath(new URL(".", import.meta.url)), "./dist"), { + recursive: true, + force: true, +}); + +const commonPlugins = () => [ + typescript({ + tsconfig: "tsconfig.json", + }), + nodeResolve(), +]; + +export default { + plugins: commonPlugins(), + input: { + client: "./src/client/index.ts", + worker: "./src/worker/index.ts", + config: "./src/scramjet.config.ts", + }, + output: { + entryFileNames: "scramjet.[name].js", + dir: "./dist", + format: "system", + bundle: true, + minify: production, + sourcemap: true, + treeshake: "recommended", + }, +}; diff --git a/src/client/cookie.ts b/src/client/cookie.ts new file mode 100644 index 0000000..230164a --- /dev/null +++ b/src/client/cookie.ts @@ -0,0 +1,10 @@ +Object.defineProperty(document, "cookie", { + get() { + return ""; + }, + set(value) { + console.log("COOKIE SET", value); + }, +}); + +delete window.cookieStore; diff --git a/src/client/index.ts b/src/client/index.ts index ffa18bd..6ac1974 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -3,6 +3,7 @@ import { decodeUrl } from "./shared"; declare global { interface Window { $s: any; + $tryset: any; $sImport: any; } } diff --git a/src/client/scope.ts b/src/client/scope.ts index 921e5a3..ef3422f 100644 --- a/src/client/scope.ts +++ b/src/client/scope.ts @@ -20,3 +20,12 @@ function scope(identifier: any) { // shorthand because this can get out of hand reall quickly window.$s = scope; + +window.$tryset = function (lhs: any, op: string, rhs: any) { + if (lhs instanceof Location) { + // @ts-ignore + locationProxy.href = rhs; + + return true; + } +}; diff --git a/src/shared/rewriters/html.ts b/src/shared/rewriters/html.ts index 2417ef2..70eb450 100644 --- a/src/shared/rewriters/html.ts +++ b/src/shared/rewriters/html.ts @@ -54,6 +54,10 @@ function traverseParsedHtml(node, origin?: URL) { } } + if (node.name === "meta" && hasAttrib(node, "http-equiv")) { + node.attribs["http-equiv"] = "a"; + } + if (hasAttrib(node, "srcdoc")) node.attribs.srcdoc = rewriteHtml(node.attribs.srcdoc, origin); if (hasAttrib(node, "style"))