summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@matthewphillips.info> 2021-03-23 13:47:54 -0400
committerGravatar GitHub <noreply@github.com> 2021-03-23 13:47:54 -0400
commit854d0feb34f605c0fe3f5627a261e327164c449e (patch)
treec7d88676affbd271fd6304a73d98c149e265f042
parent3f16550765cccee0de8f2f6e5451bf41aec13601 (diff)
downloadastro-854d0feb34f605c0fe3f5627a261e327164c449e.tar.gz
astro-854d0feb34f605c0fe3f5627a261e327164c449e.tar.zst
astro-854d0feb34f605c0fe3f5627a261e327164c449e.zip
Add support for React components. (#18)
* Add support for React components. This adds support for react components via a new `extensions` config in astro.config.mjs. In the future we can extend this to do things like look at the import statements, as Snowpack does. * Fix the tests
-rw-r--r--examples/snowpack/astro.config.mjs5
-rw-r--r--examples/snowpack/astro/layouts/post.hmx1
-rw-r--r--examples/snowpack/package-lock.json161
-rw-r--r--examples/snowpack/package.json1
-rw-r--r--package-lock.json73
-rw-r--r--package.json4
-rw-r--r--snowpack-plugin.cjs8
-rw-r--r--src/@types/astro.ts4
-rw-r--r--src/@types/compiler.ts2
-rw-r--r--src/codegen/index.ts50
-rw-r--r--src/frontend/render/react.ts28
-rw-r--r--src/runtime.ts12
-rw-r--r--test/fixtures/react-component/astro.config.mjs7
-rw-r--r--test/fixtures/react-component/astro/components/Hello.jsx5
-rw-r--r--test/fixtures/react-component/astro/pages/index.hmx10
-rw-r--r--test/fixtures/react-component/snowpack.config.js5
-rw-r--r--test/react-component.test.js40
-rw-r--r--test/snowpack-integration.test.js24
18 files changed, 388 insertions, 52 deletions
diff --git a/examples/snowpack/astro.config.mjs b/examples/snowpack/astro.config.mjs
index dbe6dc0ac..0f0be4b94 100644
--- a/examples/snowpack/astro.config.mjs
+++ b/examples/snowpack/astro.config.mjs
@@ -2,5 +2,8 @@
export default {
projectRoot: '.',
hmxRoot: './astro',
- dist: './_site'
+ dist: './_site',
+ extensions: {
+ '.jsx': 'preact'
+ }
} \ No newline at end of file
diff --git a/examples/snowpack/astro/layouts/post.hmx b/examples/snowpack/astro/layouts/post.hmx
index 357af9c68..e0b87afbf 100644
--- a/examples/snowpack/astro/layouts/post.hmx
+++ b/examples/snowpack/astro/layouts/post.hmx
@@ -72,6 +72,7 @@
.grid-body {
padding: 20px 0 0 0;
}
+ }
.header-snowpack {
font-size: 3.5rem;
diff --git a/examples/snowpack/package-lock.json b/examples/snowpack/package-lock.json
index d5d9e09a9..836a89da2 100644
--- a/examples/snowpack/package-lock.json
+++ b/examples/snowpack/package-lock.json
@@ -1269,6 +1269,8 @@
"requires": {
"@types/estree": "0.0.46",
"@types/node": "^14.14.31",
+ "@types/react": "^17.0.3",
+ "@types/react-dom": "^17.0.2",
"@vue/server-renderer": "^3.0.7",
"acorn": "^7.4.0",
"acorn-jsx": "^5.3.1",
@@ -1292,6 +1294,7 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"sass": "^1.32.8",
+ "snowpack": "^3.1.0-pre.14",
"svelte": "^3.35.0",
"vue": "^3.0.7",
"yargs-parser": "^20.2.7"
@@ -1463,6 +1466,40 @@
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA=="
},
+ "@types/prop-types": {
+ "version": "15.7.3",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
+ "dev": true
+ },
+ "@types/react": {
+ "version": "17.0.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz",
+ "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ },
+ "dependencies": {
+ "csstype": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
+ "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==",
+ "dev": true
+ }
+ }
+ },
+ "@types/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-Icd9KEgdnFfJs39KyRyr0jQ7EKhq8U6CcHRMGAS45fp5qgUvxL3ujUCfWFttUK2UErqZNj97t9gsVPNAqcwoCg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/sass": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
@@ -1471,6 +1508,12 @@
"@types/node": "*"
}
},
+ "@types/scheduler": {
+ "version": "0.16.1",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
+ "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==",
+ "dev": true
+ },
"@types/yargs-parser": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz",
@@ -1785,6 +1828,12 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
+ "big-integer": {
+ "version": "1.6.48",
+ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
+ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==",
+ "dev": true
+ },
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -1816,6 +1865,15 @@
"widest-line": "^3.1.0"
}
},
+ "bplist-parser": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
+ "integrity": "sha1-1g1dzCDLptx+HymbNdPh+V2vuuY=",
+ "dev": true,
+ "requires": {
+ "big-integer": "^1.6.7"
+ }
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1984,6 +2042,12 @@
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw=="
},
+ "cli-spinners": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz",
+ "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==",
+ "dev": true
+ },
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -2214,6 +2278,28 @@
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
"dev": true
},
+ "default-browser-id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-2.0.0.tgz",
+ "integrity": "sha1-AezONxpx6F8VoXF354YwR+c9vn0=",
+ "dev": true,
+ "requires": {
+ "bplist-parser": "^0.1.0",
+ "pify": "^2.3.0",
+ "untildify": "^2.0.0"
+ },
+ "dependencies": {
+ "untildify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz",
+ "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0"
+ }
+ }
+ }
+ },
"defer-to-connect": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
@@ -2350,6 +2436,12 @@
"integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==",
"dev": true
},
+ "esbuild": {
+ "version": "0.9.6",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.6.tgz",
+ "integrity": "sha512-F6vASxU0wT/Davt9aj2qtDwDNSkQxh9VbyO56M7PDWD+D/Vgq/rmUDGDQo7te76W5auauVojjnQr/wTu3vpaUA==",
+ "dev": true
+ },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -2631,6 +2723,12 @@
"reusify": "^1.0.4"
}
},
+ "fdir": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-5.0.0.tgz",
+ "integrity": "sha512-cteqwWMA43lEmgwOg5HSdvhVFD39vHjQDhZkRMlKmeoNPtSSgUw1nUypydiY2upMdGiBFBZvNBDbnoBh0yCzaQ==",
+ "dev": true
+ },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -3007,6 +3105,12 @@
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
"dev": true
},
+ "is-docker": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
+ "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
+ "dev": true
+ },
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -3071,6 +3175,15 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
"is-yarn-global": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
@@ -3479,6 +3592,16 @@
"wrappy": "1"
}
},
+ "open": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+ "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ }
+ },
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -3492,6 +3615,12 @@
"word-wrap": "^1.2.3"
}
},
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
"p-cancelable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
@@ -3586,6 +3715,12 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
},
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
"postcss": {
"version": "8.2.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.8.tgz",
@@ -3884,6 +4019,15 @@
"glob": "^7.1.3"
}
},
+ "rollup": {
+ "version": "2.42.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.42.3.tgz",
+ "integrity": "sha512-JjaT9WaUS5vmjy6xUrnPOskjkQg2cN4WSACNCwbOvBz8VDmbiKVdmTFUoMPRqTud0tsex8Xy9/boLbDW9HKD1w==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.1"
+ }
+ },
"run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -3990,6 +4134,23 @@
"is-fullwidth-code-point": "^3.0.0"
}
},
+ "snowpack": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.1.2.tgz",
+ "integrity": "sha512-LsYlBNjB/t/p5QP434Pa1TqjyuX8VtXiYQaAWZkOn1d1TVKEt7nigMBr8Z+EDXYn6YlLXYKHXDvv/NhUS7Ri9A==",
+ "dev": true,
+ "requires": {
+ "cli-spinners": "^2.5.0",
+ "default-browser-id": "^2.0.0",
+ "esbuild": "^0.9.3",
+ "fdir": "^5.0.0",
+ "fsevents": "^2.2.0",
+ "open": "^7.0.4",
+ "picomatch": "^2.2.2",
+ "resolve": "^1.20.0",
+ "rollup": "^2.34.0"
+ }
+ },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
diff --git a/examples/snowpack/package.json b/examples/snowpack/package.json
index a7b82b176..df38fe6e7 100644
--- a/examples/snowpack/package.json
+++ b/examples/snowpack/package.json
@@ -44,6 +44,7 @@
"postcss-preset-env": "^6.7.0",
"prettier": "^2.0.5",
"snowpack": "^3.1.0-pre.14",
+ "sass": "^1.32.8",
"stylelint": "^13.8.0",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-rational-order": "^0.1.2",
diff --git a/package-lock.json b/package-lock.json
index 4b5e338bc..edae85947 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -190,6 +190,36 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
+ "@types/prop-types": {
+ "version": "15.7.3",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
+ },
+ "@types/react": {
+ "version": "17.0.3",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz",
+ "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==",
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ },
+ "dependencies": {
+ "csstype": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz",
+ "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g=="
+ }
+ }
+ },
+ "@types/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-Icd9KEgdnFfJs39KyRyr0jQ7EKhq8U6CcHRMGAS45fp5qgUvxL3ujUCfWFttUK2UErqZNj97t9gsVPNAqcwoCg==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/sass": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.16.0.tgz",
@@ -199,6 +229,11 @@
"@types/node": "*"
}
},
+ "@types/scheduler": {
+ "version": "0.16.1",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz",
+ "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA=="
+ },
"@types/yargs-parser": {
"version": "20.2.0",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz",
@@ -527,8 +562,7 @@
"big-integer": {
"version": "1.6.48",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
- "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==",
- "dev": true
+ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
},
"big.js": {
"version": "5.2.2",
@@ -566,7 +600,6 @@
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz",
"integrity": "sha1-1g1dzCDLptx+HymbNdPh+V2vuuY=",
- "dev": true,
"requires": {
"big-integer": "^1.6.7"
}
@@ -750,8 +783,7 @@
"cli-spinners": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz",
- "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==",
- "dev": true
+ "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q=="
},
"cliui": {
"version": "7.0.4",
@@ -1005,7 +1037,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-2.0.0.tgz",
"integrity": "sha1-AezONxpx6F8VoXF354YwR+c9vn0=",
- "dev": true,
"requires": {
"bplist-parser": "^0.1.0",
"pify": "^2.3.0",
@@ -1016,7 +1047,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz",
"integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=",
- "dev": true,
"requires": {
"os-homedir": "^1.0.0"
}
@@ -1174,8 +1204,7 @@
"esbuild": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.6.tgz",
- "integrity": "sha512-F6vASxU0wT/Davt9aj2qtDwDNSkQxh9VbyO56M7PDWD+D/Vgq/rmUDGDQo7te76W5auauVojjnQr/wTu3vpaUA==",
- "dev": true
+ "integrity": "sha512-F6vASxU0wT/Davt9aj2qtDwDNSkQxh9VbyO56M7PDWD+D/Vgq/rmUDGDQo7te76W5auauVojjnQr/wTu3vpaUA=="
},
"escalade": {
"version": "3.1.1",
@@ -1493,8 +1522,7 @@
"fdir": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-5.0.0.tgz",
- "integrity": "sha512-cteqwWMA43lEmgwOg5HSdvhVFD39vHjQDhZkRMlKmeoNPtSSgUw1nUypydiY2upMdGiBFBZvNBDbnoBh0yCzaQ==",
- "dev": true
+ "integrity": "sha512-cteqwWMA43lEmgwOg5HSdvhVFD39vHjQDhZkRMlKmeoNPtSSgUw1nUypydiY2upMdGiBFBZvNBDbnoBh0yCzaQ=="
},
"file-entry-cache": {
"version": "6.0.1",
@@ -1549,8 +1577,7 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"functional-red-black-tree": {
"version": "1.0.1",
@@ -1698,7 +1725,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@@ -1882,7 +1908,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
"integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
- "dev": true,
"requires": {
"has": "^1.0.3"
}
@@ -1895,8 +1920,7 @@
"is-docker": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
- "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
- "dev": true
+ "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw=="
},
"is-extendable": {
"version": "0.1.1",
@@ -1970,7 +1994,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
"requires": {
"is-docker": "^2.0.0"
}
@@ -2230,7 +2253,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
- "dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
@@ -2397,7 +2419,6 @@
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
"integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
- "dev": true,
"requires": {
"is-docker": "^2.0.0",
"is-wsl": "^2.1.1"
@@ -2420,8 +2441,7 @@
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
- "dev": true
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"p-cancelable": {
"version": "1.1.0",
@@ -2513,8 +2533,7 @@
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
- "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
- "dev": true
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-type": {
"version": "4.0.0",
@@ -2530,8 +2549,7 @@
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"postcss": {
"version": "8.2.8",
@@ -2813,7 +2831,6 @@
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
- "dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
@@ -2853,7 +2870,6 @@
"version": "2.42.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.42.3.tgz",
"integrity": "sha512-JjaT9WaUS5vmjy6xUrnPOskjkQg2cN4WSACNCwbOvBz8VDmbiKVdmTFUoMPRqTud0tsex8Xy9/boLbDW9HKD1w==",
- "dev": true,
"requires": {
"fsevents": "~2.3.1"
}
@@ -2976,7 +2992,6 @@
"version": "3.1.0-pre.14",
"resolved": "https://registry.npmjs.org/snowpack/-/snowpack-3.1.0-pre.14.tgz",
"integrity": "sha512-NJm+XOMHryrDPWkqpqqBjQLZcSrs8WzW84H0vs3m10kFD1vvOBqPrD5ECH3fRVre314wA3arOgZbWGhY27A29w==",
- "dev": true,
"requires": {
"cli-spinners": "^2.5.0",
"default-browser-id": "^2.0.0",
diff --git a/package.json b/package.json
index 7d65fb6ec..dbdd9d596 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,8 @@
"dependencies": {
"@types/estree": "0.0.46",
"@types/node": "^14.14.31",
+ "@types/react": "^17.0.3",
+ "@types/react-dom": "^17.0.2",
"@vue/server-renderer": "^3.0.7",
"acorn": "^7.4.0",
"acorn-jsx": "^5.3.1",
@@ -50,6 +52,7 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"sass": "^1.32.8",
+ "snowpack": "^3.1.0-pre.14",
"svelte": "^3.35.0",
"vue": "^3.0.7",
"yargs-parser": "^20.2.7"
@@ -72,7 +75,6 @@
"preact": "^10.5.12",
"preact-render-to-string": "^5.1.14",
"prettier": "^2.2.1",
- "snowpack": "^3.1.0-pre.14",
"typescript": "^4.2.3",
"uvu": "^0.5.1"
}
diff --git a/snowpack-plugin.cjs b/snowpack-plugin.cjs
index 969b6075e..b4ea1b9af 100644
--- a/snowpack-plugin.cjs
+++ b/snowpack-plugin.cjs
@@ -3,7 +3,7 @@ const { readFile } = require('fs').promises;
// Snowpack plugins must be CommonJS :(
const transformPromise = import('./lib/transform2.js');
-module.exports = function (snowpackConfig, { resolve } = {}) {
+module.exports = function (snowpackConfig, { resolve, extensions } = {}) {
return {
name: 'snowpack-hmx',
knownEntrypoints: ['deepmerge'],
@@ -15,7 +15,11 @@ module.exports = function (snowpackConfig, { resolve } = {}) {
const { compileComponent } = await transformPromise;
const projectRoot = snowpackConfig.root;
const contents = await readFile(filePath, 'utf-8');
- const result = await compileComponent(contents, { compileOptions: { resolve }, filename: filePath, projectRoot });
+ const compileOptions = {
+ resolve,
+ extensions
+ };
+ const result = await compileComponent(contents, { compileOptions, filename: filePath, projectRoot });
return result.contents;
},
};
diff --git a/src/@types/astro.ts b/src/@types/astro.ts
index 69994375c..8d5979eec 100644
--- a/src/@types/astro.ts
+++ b/src/@types/astro.ts
@@ -2,12 +2,16 @@ export interface AstroConfigRaw {
dist: string;
projectRoot: string;
hmxRoot: string;
+ jsx?: string;
}
+export type ValidExtensionPlugins = 'hmx' | 'react' | 'preact' | 'svelte' | 'vue';
+
export interface AstroConfig {
dist: string;
projectRoot: URL;
hmxRoot: URL;
+ extensions?: Record<string, ValidExtensionPlugins>
}
export interface JsxItem {
diff --git a/src/@types/compiler.ts b/src/@types/compiler.ts
index 456924267..4e0ee6250 100644
--- a/src/@types/compiler.ts
+++ b/src/@types/compiler.ts
@@ -1,6 +1,8 @@
import type { LogOptions } from '../logger';
+import type { ValidExtensionPlugins } from './astro';
export interface CompileOptions {
logging: LogOptions;
resolve: (p: string) => string;
+ extensions?: Record<string, ValidExtensionPlugins>;
}
diff --git a/src/codegen/index.ts b/src/codegen/index.ts
index 725546f51..a9fc433f4 100644
--- a/src/codegen/index.ts
+++ b/src/codegen/index.ts
@@ -1,4 +1,5 @@
import type { CompileOptions } from '../@types/compiler';
+import type { ValidExtensionPlugins } from '../@types/astro';
import type { Ast, TemplateNode } from '../compiler/interfaces';
import type { JsxItem, TransformResult } from '../@types/astro';
@@ -89,10 +90,34 @@ function generateAttributes(attrs: Record<string, string>): string {
return result + '}';
}
-function getComponentWrapper(_name: string, { type, url }: { type: string; url: string }, { resolve }: CompileOptions) {
+interface ComponentInfo {
+ type: string;
+ url: string;
+}
+
+const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
+ '.hmx': 'hmx',
+ '.jsx': 'react',
+ '.vue': 'vue',
+ '.svelte': 'svelte'
+};
+
+function getComponentWrapper(_name: string, { type, url }: ComponentInfo, compileOptions: CompileOptions) {
+ const {
+ resolve,
+ extensions = defaultExtensions
+ } = compileOptions;
+
const [name, kind] = _name.split(':');
- switch (type) {
- case '.hmx': {
+
+ const plugin = extensions[type] || defaultExtensions[type];
+
+ if(!plugin) {
+ throw new Error(`No supported plugin found for extension ${type}`);
+ }
+
+ switch (plugin) {
+ case 'hmx': {
if (kind) {
throw new Error(`HMX does not support :${kind}`);
}
@@ -101,7 +126,7 @@ function getComponentWrapper(_name: string, { type, url }: { type: string; url:
wrapperImport: ``,
};
}
- case '.jsx': {
+ case 'preact': {
if (kind === 'dynamic') {
return {
wrapper: `__preact_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('preact')}')`,
@@ -114,7 +139,20 @@ function getComponentWrapper(_name: string, { type, url }: { type: string; url:
};
}
}
- case '.svelte': {
+ case 'react': {
+ if (kind === 'dynamic') {
+ return {
+ wrapper: `__react_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('react')}', '${resolve('react-dom')}')`,
+ wrapperImport: `import {__react_dynamic} from '${internalImport('render/react.js')}';`,
+ };
+ } else {
+ return {
+ wrapper: `__react_static(${name})`,
+ wrapperImport: `import {__react_static} from '${internalImport('render/react.js')}';`,
+ };
+ }
+ }
+ case 'svelte': {
if (kind === 'dynamic') {
return {
wrapper: `__svelte_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.svelte.js'))}, \`http://TEST\${import.meta.url}\`).pathname)`,
@@ -127,7 +165,7 @@ function getComponentWrapper(_name: string, { type, url }: { type: string; url:
};
}
}
- case '.vue': {
+ case 'vue': {
if (kind === 'dynamic') {
return {
wrapper: `__vue_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.vue.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('vue')}')`,
diff --git a/src/frontend/render/react.ts b/src/frontend/render/react.ts
new file mode 100644
index 000000000..d55d30c00
--- /dev/null
+++ b/src/frontend/render/react.ts
@@ -0,0 +1,28 @@
+import React from 'react';
+import ReactDOMServer from 'react-dom/server';
+
+export function __react_static(ReactComponent: any) {
+ return (attrs: Record<string, any>, ...children: any): string => {
+ let html = ReactDOMServer.renderToString(
+ React.createElement(
+ ReactComponent,
+ attrs,
+ children
+ )
+ );
+ return html;
+ };
+}
+
+export function __react_dynamic(ReactComponent: any, importUrl: string, reactUrl: string, reactDomUrl: string) {
+ const placeholderId = `placeholder_${String(Math.random())}`;
+ return (attrs: Record<string, string>, ...children: any) => {
+ return `<div id="${placeholderId}"></div><script type="module">
+ import React from '${reactUrl}';
+ import ReactDOM from '${reactDomUrl}';
+ import Component from '${importUrl}';
+
+ ReactDOM.render(React.createElement(Component, ${JSON.stringify(attrs)}), document.getElementById('${placeholderId}'));
+ </script>`;
+ };
+}
diff --git a/src/runtime.ts b/src/runtime.ts
index 18bacb6eb..ed498c331 100644
--- a/src/runtime.ts
+++ b/src/runtime.ts
@@ -96,12 +96,15 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
}
export async function createRuntime(astroConfig: AstroConfig, logging: LogOptions) {
- const { projectRoot, hmxRoot } = astroConfig;
+ const { projectRoot, hmxRoot, extensions } = astroConfig;
const internalPath = new URL('./frontend/', import.meta.url);
// Workaround for SKY-251
- const hmxPlugOptions: { resolve?: (s: string) => string } = {};
+ const hmxPlugOptions: {
+ resolve?: (s: string) => string;
+ extensions?: Record<string, string>
+ } = { extensions };
if (existsSync(new URL('./package-lock.json', projectRoot))) {
const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8');
const pkgLock = JSON.parse(pkgLockStr);
@@ -125,7 +128,10 @@ export async function createRuntime(astroConfig: AstroConfig, logging: LogOption
},
packageOptions: {
knownEntrypoints: ['preact-render-to-string'],
- external: ['@vue/server-renderer'],
+ external: [
+ '@vue/server-renderer',
+ 'node-fetch'
+ ],
},
});
const snowpack = await startSnowpackServer({
diff --git a/test/fixtures/react-component/astro.config.mjs b/test/fixtures/react-component/astro.config.mjs
new file mode 100644
index 000000000..3a3006dd8
--- /dev/null
+++ b/test/fixtures/react-component/astro.config.mjs
@@ -0,0 +1,7 @@
+
+export default {
+ projectRoot: '.',
+ hmxRoot: './astro',
+ dist: './_site'
+ // No extensions needed, React is the default.
+} \ No newline at end of file
diff --git a/test/fixtures/react-component/astro/components/Hello.jsx b/test/fixtures/react-component/astro/components/Hello.jsx
new file mode 100644
index 000000000..d4be8a8fb
--- /dev/null
+++ b/test/fixtures/react-component/astro/components/Hello.jsx
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default function({ name }) {
+ return <h2>Hello {name}!</h2>
+} \ No newline at end of file
diff --git a/test/fixtures/react-component/astro/pages/index.hmx b/test/fixtures/react-component/astro/pages/index.hmx
new file mode 100644
index 000000000..8616754ca
--- /dev/null
+++ b/test/fixtures/react-component/astro/pages/index.hmx
@@ -0,0 +1,10 @@
+<script astro>
+ import Hello from '../components/Hello.jsx';
+</script>
+
+<astro:head>
+ <!-- Head Stuff -->
+</astro:head>
+
+<h1>My page</h1>
+<Hello name="world" /> \ No newline at end of file
diff --git a/test/fixtures/react-component/snowpack.config.js b/test/fixtures/react-component/snowpack.config.js
new file mode 100644
index 000000000..2cbf0ef07
--- /dev/null
+++ b/test/fixtures/react-component/snowpack.config.js
@@ -0,0 +1,5 @@
+export default {
+ mount: {
+
+ }
+};
diff --git a/test/react-component.test.js b/test/react-component.test.js
new file mode 100644
index 000000000..0b6273922
--- /dev/null
+++ b/test/react-component.test.js
@@ -0,0 +1,40 @@
+import { suite } from 'uvu';
+import * as assert from 'uvu/assert';
+import { createRuntime } from '../lib/runtime.js';
+import { loadConfig } from '../lib/config.js';
+import { doc } from './test-utils.js';
+
+const React = suite('React Components');
+
+let runtime;
+
+React.before(async () => {
+ const astroConfig = await loadConfig(new URL('./fixtures/react-component', import.meta.url).pathname);
+
+ const logging = {
+ level: 'error',
+ dest: process.stderr
+ };
+
+ try {
+ runtime = await createRuntime(astroConfig, logging);
+ } catch(err) {
+ console.error(err);
+ throw err;
+ }
+});
+
+React.after(async () => {
+ await runtime.shutdown();
+});
+
+React('Can load hmx page', async () => {
+ const result = await runtime.load('/');
+
+ assert.equal(result.statusCode, 200);
+
+ const $ = doc(result.contents);
+ assert.equal($('h2').text(), 'Hello world!');
+});
+
+React.run(); \ No newline at end of file
diff --git a/test/snowpack-integration.test.js b/test/snowpack-integration.test.js
index c851e8bdb..8547ee7cd 100644
--- a/test/snowpack-integration.test.js
+++ b/test/snowpack-integration.test.js
@@ -1,35 +1,39 @@
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { createRuntime } from '../lib/runtime.js';
+import { loadConfig } from '../lib/config.js';
import { promises as fsPromises } from 'fs';
import { relative as pathRelative } from 'path';
import { doc } from './test-utils.js';
const { readdir, stat } = fsPromises;
-// Bug: Snowpack config is still loaded relative to the current working directory.
-process.chdir(new URL('../examples/snowpack/', import.meta.url).pathname);
-
const SnowpackDev = suite('snowpack.dev');
-let runtime;
+let runtime, cwd;
SnowpackDev.before(async () => {
- const astroConfig = {
- projectRoot: new URL('../examples/snowpack/', import.meta.url),
- hmxRoot: new URL('../examples/snowpack/astro/', import.meta.url),
- dist: './_site',
- };
+// Bug: Snowpack config is still loaded relative to the current working directory.
+ cwd = process.cwd();
+ process.chdir(new URL('../examples/snowpack/', import.meta.url).pathname);
+
+ const astroConfig = await loadConfig(new URL('../examples/snowpack', import.meta.url).pathname);
const logging = {
level: 'error',
dest: process.stderr,
};
- runtime = await createRuntime(astroConfig, logging);
+ try {
+ runtime = await createRuntime(astroConfig, logging);
+ } catch(err) {
+ console.error(err);
+ throw err;
+ }
});
SnowpackDev.after(async () => {
+ process.chdir(cwd);
await runtime && runtime.shutdown();
});