Skip to content

Update to 0.15.0; use ES Module Shims #275

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

Merged
merged 35 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
00ed3da
Update changelog for last release
JordanMartinez Mar 31, 2022
098fb42
Update purescript deps for 0.15.0; drop cst dep
JordanMartinez Apr 30, 2022
905d54d
Upgrade to latest pkg set; install entire set
JordanMartinez Apr 30, 2022
3880074
Get server code to compile
JordanMartinez Apr 30, 2022
37cf79a
Migrated FFI to ES modules via 'lebab'
JordanMartinez Apr 30, 2022
db55540
Removed '"use strict";' in FFI files
JordanMartinez Apr 30, 2022
479619b
Remove redundant import
JordanMartinez Apr 30, 2022
8474470
Update package set; install entire set
JordanMartinez May 19, 2022
91f18d1
Update PureScript to 0.15.2
JordanMartinez May 19, 2022
84f9908
Use ES Modules Shims for iframe shims
JordanMartinez May 19, 2022
f436199
Add text to call the `main` function
JordanMartinez May 19, 2022
37cdfc8
Update es-module-shims to 1.5.5 (latest)
JordanMartinez May 19, 2022
8fa99e8
Move import path update into client
JordanMartinez May 19, 2022
b15c61b
Revert "Update es-module-shims to 1.5.5 (latest)"
JordanMartinez May 19, 2022
7366787
Update readme
JordanMartinez May 19, 2022
76a5345
Bundle code via esbuild after remapping imports
JordanMartinez May 19, 2022
29c602b
Revert "Bundle code via esbuild after remapping imports"
JordanMartinez May 19, 2022
121f928
Use 1.5.5 version of es-module-shims w/ integrity
JordanMartinez May 19, 2022
3309843
Set base to fix import remapping issue
JordanMartinez May 19, 2022
60730cb
Ensure we only ever run `main()` once
JordanMartinez May 19, 2022
37fb6cf
Cleanup imports
JordanMartinez May 19, 2022
16aaff7
Fix imports using config url
JordanMartinez May 19, 2022
e4b49d2
Add serve:production script
JordanMartinez May 19, 2022
40f0d95
Add instructions for adding shims to import map
JordanMartinez May 19, 2022
f671cbc
Doc how to calc integrity hash
JordanMartinez May 19, 2022
6b81543
Fix rendering of bullet list
JordanMartinez May 19, 2022
27eb703
Remap import paths via base elem set at build time
JordanMartinez May 20, 2022
f9c30d6
Drop preloaders of shims
JordanMartinez May 23, 2022
c059482
Cleanup code appending script to body
JordanMartinez May 23, 2022
a993bc6
Stop infinite `main` invocation when DOM modified
JordanMartinez May 26, 2022
9a97914
Update package set to latest; install all packages
JordanMartinez May 26, 2022
0fb4edc
Drop workaround; modify `main` el instead
JordanMartinez May 26, 2022
0584223
Update base to production; fail CI if not that
JordanMartinez May 28, 2022
0369198
Update readme to mention base script
JordanMartinez Jun 8, 2022
1cc3451
Update to latest package set; install entire set
JordanMartinez Jun 8, 2022
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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ jobs:
npm run build
npm run test
npm run build:production
if [ "/output/ignored/" != "$(cat public/frame.html | grep '<base' | sed -E 's/ +<base href="([^"]+)">/\1/;')" ]; then
echo "client/public/frame.html's 'base' element refers to the wrong path"
echo "Run 'npm run base:production' in the 'client' dir, and push the update as a commit"
echo "CI will fail until then"
exit 1
fi

- name: Build client assets
if: github.event_name == 'release'
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ Notable changes to this project are documented in this file. The format is based

## [Unreleased]

Breaking changes:

New features:

Bugfixes:

Other improvements:

## [v2022-02-25.1](https://github.com/purescript/trypurescript/releases/tag/v2022-02-25.1)

Breaking changes:
- Update compiler to v0.14.7 (#271 by @JordanMartinez)

Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ ln -s "$PWD/output" "$PWD/../client/public/js/output"
cd client
npm install

# Use `build:dev` if you are using a local Try PureScript server, e.g. you
# followed the instructions in step 1.
# Use `build:dev` and `base:dev` if you are using a local Try PureScript server,
# e.g. you followed the instructions in step 1.
#
# Use `build:production` if you would like to test the client against the production
# Try PureScript server.
# Use `build:production` and `base:production` if you would like
# to test the client against the production Try PureScript server.
npm run build:(dev|production)
npm run base:(dev|production)

cd public
npx http-server # Try PureScript is now available on localhost:8080
npm run serve # Try PureScript is now available on localhost:8080
```

### 4. Choosing a Tag
Expand Down
18 changes: 18 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,21 @@ Update the package set by doing the following:
```
$ spago ls packages | cut -f 1 -d ' ' | xargs spago install
```

4. If any packages need NPM dependencies, you can try adding their shims to the import map in `client/public/frame.html`
- Open up the `generator.jspm.io` URL in the comment
- Use the 'Add Dependency' search bar to find the NPM dependency
- If it exists but doesn't exist in that CDN, you can try another one or [open an issue on `jspm/project`](https://github.com/jspm/project#issue-queue-for-the-jspm-cdn)
- Update the version to the one you need once added
- If needed, include other files from that dependency
- Copy and paste the content into the `client/public/frame.html` file
- Ensure `es-module-shims` has version `1.5.5` or greater.

5. If `es-module-shims` releases a new version, you can calculate its SHA-384 via

```console
$ ESM_VERSION=1.5.5
$ curl -L -o es-module-shims.js "https://ga.jspm.io/npm:es-module-shims@$ESM_VERSION/dist/es-module-shims.js"
$ echo "sha384-$(openssl dgst -sha384 -binary es-module-shims.js | openssl base64 -A)"
$ rm es-module-shims.js
```
3 changes: 0 additions & 3 deletions client/config/dev/Try.Config.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ module Try.Config where

import Prelude

loaderUrl :: String
loaderUrl = "js/output"

compileUrl :: String
compileUrl = "http://localhost:8081"

Expand Down
3 changes: 0 additions & 3 deletions client/config/prod/Try.Config.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ module Try.Config where

import Prelude

loaderUrl :: String
loaderUrl = "https://compile.purescript.org/output"

compileUrl :: String
compileUrl = "https://compile.purescript.org"

Expand Down
8 changes: 7 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
"private": true,
"scripts": {
"clean": "rimraf output",
"base:dev": "node setBase.mjs \"dev\"",
"base:production": "node setBase.mjs \"prod\"",
"test": "spago test --path config/dev/Try.Config.purs",
"build": "spago build --path config/dev/Try.Config.purs",
"build:dev": "spago bundle-app --path config/dev/Try.Config.purs --to public/js/index.js",
"build:production": "spago bundle-app --path config/prod/Try.Config.purs --purs-args '--censor-lib --strict' --to public/js/index.js"
"build:production": "spago bundle-app --path config/prod/Try.Config.purs --purs-args '--censor-lib --strict' --to public/js/index.js",
"serve": "http-server public/ -o / --cors=\"Access-Control-Allow-Origin: *\" -c-1",
"serve:dev": "npm run build:dev && npm run base:dev && npm run serve",
"serve:production": "npm run build:production && npm run base:production && npm run serve"
},
"devDependencies": {
"http-server": "^14.1.0",
"purescript": "^0.13.6",
"purescript-psa": "^0.7.3",
"rimraf": "^2.5.4",
Expand Down
44 changes: 42 additions & 2 deletions client/public/frame.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,49 @@
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/frame.js"></script>
<!-- Do not edit the base element's 'href' manually. Use the 'base:dev' or 'base:production' script -->
<base href="/output/ignored/">
<script>
window.esmsInitOptions = {
// -- Hooks --
// Module load error
onerror: (e) => {
console.log("Error while loading module: ");
console.log(e);
throw e;
},
};
</script>
<!--
JSPM Generator Import Map
Edit URL: https://generator.jspm.io/#Y2NnYGCzD80rySzJSU1hSMpM183MK0lNTy1yMNQz0zM1ZEhJTc7MTczRyyp2MDTQM9YzZChKTUwu0U3Jz3UwNNMzxCqiX5xaVJZaBJGAKystzUxxsACaYQQAoBlP83cA
-->
<script type="importmap">
{
"imports": {
"big-integer": "https://ga.jspm.io/npm:[email protected]/BigInteger.js",
"decimal.js": "https://ga.jspm.io/npm:[email protected]/decimal.js",
"react": "https://ga.jspm.io/npm:[email protected]/index.js",
"react-dom": "https://ga.jspm.io/npm:[email protected]/index.js",
"react-dom/server": "https://ga.jspm.io/npm:[email protected]/server.browser.js",
"uuid": "https://ga.jspm.io/npm:[email protected]/dist/esm-browser/index.js"
},
"scopes": {
"https://ga.jspm.io/": {
"object-assign": "https://ga.jspm.io/npm:[email protected]/index.js",
"react": "https://ga.jspm.io/npm:[email protected]/index.js",
"scheduler": "https://ga.jspm.io/npm:[email protected]/index.js"
}
}
}
</script>

<!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
<script async src="https://ga.jspm.io/npm:[email protected]/dist/es-module-shims.js" integrity="sha384-Zt+0efULC2q2dftjz0uNzXeTpPVuSLLekXQv9HoRuigkAyLPaUFvPVpYYhu2Xc/t" crossorigin="anonymous"></script>

<script src="/js/frame.js"></script>
</head>
<body>
<main id="main"></main>
<main id="main"></main>
</body>
</html>
40 changes: 8 additions & 32 deletions client/public/js/frame.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,18 @@
(function() {
function evalSources(sources) {
var modules = {};
function dirname(str) {
var ix = str.lastIndexOf("/");
return ix < 0 ? "" : str.slice(0, ix);
}
function resolvePath(a, b) {
if (b[0] === "." && b[1] === "/") {
return dirname(a) + b.slice(1);
}
if (b[0] === "." && b[1] === "." && b[2] === "/") {
return dirname(dirname(a)) + b.slice(2);
}
return b;
}
return function load(name) {
if (modules[name]) {
return modules[name].exports;
}
function require(path) {
return load(resolvePath(name, path));
}
var module = modules[name] = { exports: {} };
new Function("module", "exports", "require", sources[name])(module, module.exports, require);
return module.exports;
};
}

var parent;

document.addEventListener("DOMContentLoaded", function() {
window.addEventListener("message", function(event) {
parent = event.source;
parent.postMessage("trypurescript", "*");
var file = evalSources(event.data)("<file>");
if (file.main && typeof file.main === "function") {
file.main();
}
const code = `
${event.data.code}
main();
`;
const scriptEl = document.createElement("script");
scriptEl.type = "module";
scriptEl.appendChild(document.createTextNode(code));
document.body.appendChild(scriptEl);
}, { once: true });
}, { once: true });

Expand Down
28 changes: 28 additions & 0 deletions client/setBase.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env node

import path from "path";
import fs from "fs";
import process from "process";

const filePath = path.join("public", "frame.html");

// PureScript generates JS with the following import lines:
// `import * as Data_Foo from "../Data.Foo/index.js"
// To remap `../Data.Foo/index.js` to `output/Data.Foo/index.js`
// we append `/ignored/`.
//
// This: `/output/ignored/../Data.Foo/index.js`
// becomes: `/output/Data.Foo/index.js`
const prodPath = "/output/ignored/"
const devPath = "/js/output/ignored/"

const environment = process.argv[2] || "dev";
const baseHref = environment === "prod" ? prodPath : devPath;

const frameHtml = fs.readFileSync(filePath, "utf-8");
const newHtml = frameHtml
.split("\n")
.map((line) => line.replace(/^( *<base href=")[^"]*(".+)$/, `$1${baseHref}$2`))
.join("\n");

fs.writeFileSync(filePath, newHtml);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have to rewrite the file in place, I'm not sure this is the best way to do this. Will this dirty the source tree?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also curious how this works with the actual prod deployment of the UI. I'm not fully aware of all that goes into that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it would dirty the source tree.

The client is built in the ci.yml file by using NPM scripts before packaging up the results. When we deploy the code, the server downloads the client.tar.gz, unpacks it, and then runs it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should this issue be resolved? Do we create 2 versions of the frame.html file, one with base set to one and another with base set to something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a check in CI to fail the build if the base element does not refer to the production path. One can use npm run base:dev to test out their code in development and then run npm run base:production before committing the final results. This ensures the following:

  • local dev still works without much friction in workflow
  • the source tree is clean when the production build is made
  • frame.html isn't duplicated, so there's only one place to update the import maps

I believe that resolves your final issue with this PR.

28 changes: 8 additions & 20 deletions client/src/Try/Container.purs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import Effect.Aff (Aff, makeAff)
import Effect.Aff as Aff
import Effect.Class.Console (error)
import Effect.Uncurried (EffectFn3, runEffectFn3)
import Foreign.Object (Object)
import Foreign.Object as Object
import Halogen as H
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
Expand All @@ -29,10 +27,8 @@ import Try.Editor (MarkerType(..), toStringMarkerType)
import Try.Editor as Editor
import Try.Gist (getGistById, tryLoadFileFromGist)
import Try.GitHub (getRawGitHubFile)
import Try.Loader (Loader, makeLoader, runLoader)
import Try.QueryString (getQueryStringMaybe)
import Try.Session (createSessionIdIfNecessary, storeSession, tryRetrieveSession)
import Try.Types (JS(..))
import Web.HTML (window)
import Web.HTML.Window (alert)

Expand Down Expand Up @@ -83,12 +79,9 @@ data Action
_editor :: SProxy "editor"
_editor = SProxy

loader :: Loader
loader = makeLoader Config.loaderUrl

type LoadCb = Effect Unit
type FailCb = Effect Unit
foreign import setupIFrame :: EffectFn3 (Object JS) LoadCb FailCb Unit
foreign import setupIFrame :: EffectFn3 { code :: String } LoadCb FailCb Unit
foreign import teardownIFrame :: Effect Unit

component :: forall q i o. H.Component HH.HTML q i o Aff
Expand Down Expand Up @@ -189,22 +182,17 @@ component = H.mkComponent
if settings.showJs then
H.liftEffect teardownIFrame
else do
eitherSources <- H.liftAff $ runExceptT $ runLoader loader (JS js)
for_ warnings \warnings_ -> do
let anns = Array.mapMaybe (toAnnotation MarkerWarning) warnings_
_ <- H.query _editor unit $ H.tell $ Editor.SetAnnotations anns
pure unit
case eitherSources of
Right sources -> do
let eventData = Object.insert "<file>" (JS js) sources
H.liftAff $ makeAff \f -> do
runEffectFn3 setupIFrame eventData (f (Right unit)) (f (Left $ Aff.error "Could not load iframe"))
mempty
H.modify_ _ { compiled = Just (Right res) }
Left err -> do
H.liftEffect teardownIFrame
H.liftEffect $ error err
H.modify_ _ { compiled = Just (Left err) }
let
eventData = { code: js }
H.liftEffect teardownIFrame
H.liftAff $ makeAff \f -> do
runEffectFn3 setupIFrame eventData (f (Right unit)) (f (Left $ Aff.error "Could not load iframe"))
mempty
H.modify_ _ { compiled = Just (Right res) }

HandleEditor (Editor.TextChanged text) -> do
_ <- H.fork $ handleAction $ Cache text
Expand Down
Loading