mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-28 06:05:31 +03:00
Merge branch 'master' into phantom-builder
This commit is contained in:
commit
a6dcde5dde
@ -22,6 +22,24 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Y0hy0h",
|
||||
"name": "Johannes Maas",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/11377826?v=4",
|
||||
"profile": "https://github.com/Y0hy0h",
|
||||
"contributions": [
|
||||
"userTesting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vViktorPL",
|
||||
"name": "Wiktor Toporek",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2961541?v=4",
|
||||
"profile": "https://github.com/vViktorPL",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
119
.github/workflows/ci.yml
vendored
Normal file
119
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
name: Elm CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 13
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 13
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
- uses: actions/cache@v1
|
||||
id: elm-cache
|
||||
with:
|
||||
path: ~/.elm
|
||||
key: ${{ runner.os }}-elm-home-${{ hashFiles('**/elm.json') }}
|
||||
- run: npm ci
|
||||
- run: ./node_modules/.bin/elm make --output /dev/null && cd generator && ../node_modules/.bin/elm make --output /dev/null src/Main.elm
|
||||
- run: npm test
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 13
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 13
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
- uses: actions/cache@v1
|
||||
id: elm-cache
|
||||
with:
|
||||
path: ~/.elm
|
||||
key: ${{ runner.os }}-elm-home-${{ hashFiles('**/elm.json') }}
|
||||
- run: npm ci
|
||||
- run: ./node_modules/.bin/elm make --output /dev/null && cd generator && ../node_modules/.bin/elm make --output /dev/null src/Main.elm
|
||||
- name: elm-format
|
||||
run: npx --no-install elm-format --validate
|
||||
|
||||
validate-package:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 13
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 13
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
- uses: actions/cache@v1
|
||||
id: elm-cache
|
||||
with:
|
||||
path: ~/.elm
|
||||
key: ${{ runner.os }}-elm-home-${{ hashFiles('**/elm.json') }}
|
||||
- run: npm ci
|
||||
- run: ./node_modules/.bin/elm make --output /dev/null && cd generator && ../node_modules/.bin/elm make --output /dev/null src/Main.elm
|
||||
- name: Build elm docs
|
||||
run: ./node_modules/.bin/elm make --docs docs.json
|
||||
|
||||
publish:
|
||||
needs: [test, lint, validate-package]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 13
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 13
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
- uses: actions/cache@v1
|
||||
id: elm-cache
|
||||
with:
|
||||
path: ~/.elm
|
||||
key: ${{ runner.os }}-elm-home-${{ hashFiles('**/elm.json') }}
|
||||
- run: npm ci
|
||||
- run: ./node_modules/.bin/elm make --output /dev/null && cd generator && ../node_modules/.bin/elm make --output /dev/null src/Main.elm
|
||||
- name: Elm Publish
|
||||
uses: dillonkearns/elm-publish-action@1.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-elm: ./node_modules/.bin/elm
|
||||
- run: npm run build
|
||||
- id: npm-publish
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
19
.travis.yml
19
.travis.yml
@ -1,19 +0,0 @@
|
||||
language: elm
|
||||
elm: latest-0.19.1
|
||||
elm-test: latest-0.19.1
|
||||
elm-format: 0.8.2
|
||||
|
||||
install:
|
||||
- npm ci
|
||||
|
||||
script:
|
||||
- npm test
|
||||
|
||||
deploy:
|
||||
- provider: npm
|
||||
email: dillon@dillonkearns.com
|
||||
api_key: $NPM_TOKEN
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_TAG =~ ^v
|
@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [4.0.1] - 2020-03-28
|
||||
|
||||
### Added
|
||||
- You can now host your `elm-pages` site in a sub-directory. For example, you could host it at mysite.com/blog, where the top-level mysite.com/ is hosting a different app.
|
||||
This works using [HTML `<base>` tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base). The paths you get from `PagePath.toString` and `ImagePath.toString`
|
||||
will use relative paths (e.g. `blog/my-article`) instead of absolute URLs (e.g. `/blog/my-article`), so you can take advantage of this functionality by just making sure you
|
||||
use the path helpers and don't hardcode any absolute URL strings. See https://github.com/dillonkearns/elm-pages/pull/73.
|
||||
|
||||
## [4.0.0] - 2020-03-04
|
||||
|
||||
### Changed
|
||||
|
@ -9,6 +9,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.2.12] - 2020-03-28
|
||||
|
||||
### Added
|
||||
- You can now host your `elm-pages` site in a sub-directory. For example, you could host it at mysite.com/blog, where the top-level mysite.com/ is hosting a different app.
|
||||
This works using [HTML `<base>` tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base). The paths you get from `PagePath.toString` and `ImagePath.toString`
|
||||
will use relative paths (e.g. `blog/my-article`) instead of absolute URLs (e.g. `/blog/my-article`), so you can take advantage of this functionality by just making sure you
|
||||
use the path helpers and don't hardcode any absolute URL strings. See https://github.com/dillonkearns/elm-pages/pull/73.
|
||||
|
||||
## [1.2.11] - 2020-03-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Triple quoted strings in content files are now escaped properly (see [#26](https://github.com/dillonkearns/elm-pages/issues/26)).
|
||||
- Fixed a path delimiter bug for Windows. Dev server appears to work smoothly on Windows now. See [#82](https://github.com/dillonkearns/elm-pages/pull/82).
|
||||
There's currently an issue with running a production build on windows because of Google Closure Compiler. We're investigating possible fixes.
|
||||
A big thank you [@vViktorPL](https://github.com/vViktorPL) for these two fixes!
|
||||
|
||||
## [1.2.10] - 2020-02-25
|
||||
|
||||
- Turn off offline service worker fallbacks for now. This will likely be revisited
|
||||
@ -24,116 +41,136 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
## [1.2.8] - 2020-02-08
|
||||
|
||||
### Fixed
|
||||
|
||||
- Colorize elm make output for initial elm-pages build step. See [#66](https://github.com/dillonkearns/elm-pages/issues/66).
|
||||
Note, this patch still hasn't propogated through to `elm-webpack-loader` (see https://github.com/elm-community/elm-webpack-loader/issues/166).
|
||||
So there may still be non-colorized output for errors as you make changes while the dev server is running.
|
||||
Note, this patch still hasn't propogated through to `elm-webpack-loader` (see https://github.com/elm-community/elm-webpack-loader/issues/166).
|
||||
So there may still be non-colorized output for errors as you make changes while the dev server is running.
|
||||
|
||||
## [1.2.7] - 2020-02-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't serve fallback HTML from service worker when a page 404s... only when it fails to load (i.e. when
|
||||
you're offline). 404s will go through from the server if you're online now.
|
||||
you're offline). 404s will go through from the server if you're online now.
|
||||
|
||||
## [1.2.6] - 2020-02-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Only serve up the root route's HTML as a fallback when you're offline. This fixes the flash of root page content
|
||||
when you are online. When you're offline, you will currently still see the root page flash when you load a page,
|
||||
but you will be able to navigate to any cached pages as long as their content.json is in the service worker cache.
|
||||
when you are online. When you're offline, you will currently still see the root page flash when you load a page,
|
||||
but you will be able to navigate to any cached pages as long as their content.json is in the service worker cache.
|
||||
|
||||
## [1.2.5] - 2020-01-31
|
||||
|
||||
### Fixed
|
||||
|
||||
- Make sure that pre-render trigger event fires to fix pre-rendering hanging.
|
||||
|
||||
## [1.2.4] - 2020-01-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't pre-fetch content.json files for unknown paths: https://github.com/dillonkearns/elm-pages/pull/60.
|
||||
- Fix race condition where pre-rendered content sometimes didn't have body: https://github.com/dillonkearns/elm-pages/pull/62.
|
||||
|
||||
## [1.2.2] - 2020-01-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dev server only terminates with unrecoverable build errors, and now will
|
||||
continue running with recoverable errors like metadata parsing errors.
|
||||
See [#58](https://github.com/dillonkearns/elm-pages/pull/58).
|
||||
continue running with recoverable errors like metadata parsing errors.
|
||||
See [#58](https://github.com/dillonkearns/elm-pages/pull/58).
|
||||
|
||||
### Added
|
||||
|
||||
- The `pagesInit` function that wraps the way you initialize your app in `index.js` now returns a Promise
|
||||
so you can wire up ports to it once it's initialized. See [#50](https://github.com/dillonkearns/elm-pages/pull/50).
|
||||
Thank you [@icidasset](https://github.com/icidasset)! 🙏
|
||||
so you can wire up ports to it once it's initialized. See [#50](https://github.com/dillonkearns/elm-pages/pull/50).
|
||||
Thank you [@icidasset](https://github.com/icidasset)! 🙏
|
||||
|
||||
## [1.2.1] - 2020-01-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- Removed a couple of debug console.log statements from the CLI.
|
||||
|
||||
## [1.2.0] - 2020-01-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed the CLI generator to expect code from the new Elm package from the new
|
||||
`generateFiles` hook in `Pages.Platform.application`.
|
||||
`generateFiles` hook in `Pages.Platform.application`.
|
||||
|
||||
## [1.1.8] - 2020-01-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- "Missing content" message no longer flashes between pre-rendered HTML and the Elm app hydrating and taking over the page. See [#48](https://github.com/dillonkearns/elm-pages/pull/48).
|
||||
|
||||
## [1.1.7] - 2020-01-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Newlines and escaped double quotes (`"`s) are handled properly in content frontmatter now. See [#41](https://github.com/dillonkearns/elm-pages/pull/41). Thank you [Luke](https://github.com/lukewestby)! 🎉🙏
|
||||
|
||||
## [1.1.6] - 2020-01-04
|
||||
|
||||
### Added
|
||||
|
||||
- Added hot reloading for code changes! That means that in dev mode (`elm-pages develop`),
|
||||
you can change your code and the changes will be reloaded in your browser for you instantly.
|
||||
Note that changing files in your `content` folder won't yet be instantly reloaded, that will
|
||||
be a future task. See [#35](https://github.com/dillonkearns/elm-pages/pull/35).
|
||||
you can change your code and the changes will be reloaded in your browser for you instantly.
|
||||
Note that changing files in your `content` folder won't yet be instantly reloaded, that will
|
||||
be a future task. See [#35](https://github.com/dillonkearns/elm-pages/pull/35).
|
||||
|
||||
## [1.1.5] - 2020-01-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the bug that showed blank pages and failed page reloads when you change files in the `content` folder. Thank you so much [@danmarcab](https://github.com/danmarcab) for contributing the fix! See [#23](https://github.com/dillonkearns/elm-pages/pull/23).
|
||||
|
||||
## [1.1.4] - 2020-01-03
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated `favicons-webpack-plugin` to latest version. Had to upgrade to `html-webpack-plugin@4.0.0-beta.11`
|
||||
for this. See [#32](https://github.com/dillonkearns/elm-pages/issues/32).
|
||||
|
||||
## [1.1.3] - 2020-01-03
|
||||
|
||||
*Check out [this upgrade checklist](https://github.com/dillonkearns/elm-pages/blob/master/docs/upgrade-guide.md#upgrading-to-elm-package-110-and-npm-package-113) for more details and steps for upgrading your project.
|
||||
\*Check out [this upgrade checklist](https://github.com/dillonkearns/elm-pages/blob/master/docs/upgrade-guide.md#upgrading-to-elm-package-110-and-npm-package-113) for more details and steps for upgrading your project.
|
||||
|
||||
### Changed
|
||||
|
||||
- Added `StaticHttp` requests in the CLI process (see the Elm package changelog).
|
||||
|
||||
## [1.0.41] - 2019-11-14
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a regression where elm-markup frontmatter was being incorrectly parsed as JSON
|
||||
(fixes [#20](https://github.com/dillonkearns/elm-pages/issues/20)).
|
||||
(fixes [#20](https://github.com/dillonkearns/elm-pages/issues/20)).
|
||||
|
||||
## [1.0.40] - 2019-11-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- Generate files for extensions other than `.md` and `.emu` (fixes [#16](https://github.com/dillonkearns/elm-pages/issues/16)).
|
||||
As always, be sure to also use the latest Elm package.
|
||||
As always, be sure to also use the latest Elm package.
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to use a custom port for dev server ([#10](https://github.com/dillonkearns/elm-pages/pull/10); thank you [@leojpod](https://github.com/leojpod)! 🎉)
|
||||
|
||||
## [1.0.39] - 2019-10-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use hidden `<div>` to listen for Elm view renders instead of wrapping entire
|
||||
page in an extra div. Fixes [#5](https://github.com/dillonkearns/elm-pages/issues/5).
|
||||
page in an extra div. Fixes [#5](https://github.com/dillonkearns/elm-pages/issues/5).
|
||||
|
||||
### Changed
|
||||
|
||||
- Add `onPageChange : PagePath Pages.PathKey -> userMsg` field to `Pages.application` config record.
|
||||
This is analagous to `onUrlChange` in `Browser.application`, except that you get a
|
||||
type-safe `PagePath Pages.PathKey` because it is guaranteed that you will only
|
||||
go to one of your static routes when this `Msg` is fired. Fixes [#4](https://github.com/dillonkearns/elm-pages/issues/4).
|
||||
This is analagous to `onUrlChange` in `Browser.application`, except that you get a
|
||||
type-safe `PagePath Pages.PathKey` because it is guaranteed that you will only
|
||||
go to one of your static routes when this `Msg` is fired. Fixes [#4](https://github.com/dillonkearns/elm-pages/issues/4).
|
||||
|
12
README.md
12
README.md
@ -1,6 +1,9 @@
|
||||
# `elm-pages` [![Netlify Status](https://api.netlify.com/api/v1/badges/8ee4a674-4f37-4f16-b99e-607c0a02ee75/deploy-status)](https://app.netlify.com/sites/elm-pages/deploys) [![Build Status](https://travis-ci.org/dillonkearns/elm-pages.svg?branch=master)](https://travis-ci.org/dillonkearns/elm-pages) [![npm](https://img.shields.io/npm/v/elm-pages.svg)](https://npmjs.com/package/elm-pages) [![Elm package](https://img.shields.io/elm-package/v/dillonkearns/elm-pages.svg)](https://package.elm-lang.org/packages/dillonkearns/elm-pages/latest/)
|
||||
# `elm-pages` [![Netlify Status](https://api.netlify.com/api/v1/badges/8ee4a674-4f37-4f16-b99e-607c0a02ee75/deploy-status)](https://app.netlify.com/sites/elm-pages/deploys) [![Build Status](https://github.com/dillonkearns/elm-pages/workflows/Elm%20CI/badge.svg)](https://github.com/dillonkearns/elm-pages/actions?query=branch%3Amaster) [![npm](https://img.shields.io/npm/v/elm-pages.svg)](https://npmjs.com/package/elm-pages) [![Elm package](https://img.shields.io/elm-package/v/dillonkearns/elm-pages.svg)](https://package.elm-lang.org/packages/dillonkearns/elm-pages/latest/)
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)
|
||||
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-)
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/dillonkearns/elm-pages-starter)
|
||||
@ -160,11 +163,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/danmarcab"><img src="https://avatars2.githubusercontent.com/u/1517969?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Marín</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=danmarcab" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://citric.id"><img src="https://avatars1.githubusercontent.com/u/296665?v=4" width="100px;" alt=""/><br /><sub><b>Steven Vandevelde</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=icidasset" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Y0hy0h"><img src="https://avatars0.githubusercontent.com/u/11377826?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Maas</b></sub></a><br /><a href="#userTesting-Y0hy0h" title="User Testing">📓</a></td>
|
||||
<td align="center"><a href="https://github.com/vViktorPL"><img src="https://avatars1.githubusercontent.com/u/2961541?v=4" width="100px;" alt=""/><br /><sub><b>Wiktor Toporek</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=vViktorPL" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
22
docs/FAQ.md
Normal file
22
docs/FAQ.md
Normal file
@ -0,0 +1,22 @@
|
||||
## Can you pass flags in to your `elm-pages` app?
|
||||
There's no way to pass flags in right now. I'm collecting use cases and trying to figure out what the most intuitive thing would be conceptual, given that the value of flags will be different during Pre-Rendering and Client-Side Rendering.
|
||||
So for example, if you get the window dimensions from the flags and do responsive design based on that, then you'll see a flash after the client-side code takes over since it will run with a different value for flags. So that semantics of the flags are not quite intuitive there. You can achieve the same thing with a port, but the semantics are a little more obvious there because you now have to explicitly say how to handle the case where you don't have access to flags.
|
||||
|
||||
|
||||
## How do you handle responsive layouts when you don't the browser dimensions at build time?
|
||||
|
||||
A lot of users are building their `elm-pages` views with `elm-ui`, so this is a common question because
|
||||
`elm-ui` is designed to do responsive layouts by storing the browser dimensions in the Model and
|
||||
doing conditionals based on that state.
|
||||
|
||||
With `elm-pages`, and static sites in general, we are building pre-rendered HTML so we can serve it up
|
||||
really quickly through a CDN, rather than serving it up with a traditional server framework. That means
|
||||
that to have responsive pages that don't have a page flash, we need to use media queries to make our pages responsive.
|
||||
That way, the view is the same no matter what the dimensions are, so it will pre-render and look right on whatever
|
||||
device the user is on because the media queries will take care of making it responsive.
|
||||
|
||||
Since `elm-ui` isn't currently built with media queries in mind, it isn't a first-class experience to use them with
|
||||
`elm-ui`. One workaround you can use is to define some responsive classes that simply show or hide an element based on
|
||||
a media query, and apply those classes. For example, you could show the mobile or desktop version of the navbar
|
||||
by having a `mobile-responsive` and `desktop-responsive` class and rendering one element with each respsective class.
|
||||
But the media query will only show one at a time based on the dimensions.
|
2
elm.json
2
elm.json
@ -3,7 +3,7 @@
|
||||
"name": "dillonkearns/elm-pages",
|
||||
"summary": "A statically typed site generator.",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"exposed-modules": [
|
||||
"Head",
|
||||
"Head.Seo",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"title": "Extensible Markdown Parsing in Pure Elm",
|
||||
"description": "Introducing a new parser that extends your palette with no additional syntax",
|
||||
"image": "/images/article-covers/extensible-markdown-parsing.jpg",
|
||||
"image": "images/article-covers/extensible-markdown-parsing.jpg",
|
||||
"published": "2019-10-08",
|
||||
}
|
||||
---
|
||||
|
@ -5,7 +5,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"title": "Generating Files with Pure Elm",
|
||||
"description": "Introducing a new parser that extends your palette with no additional syntax",
|
||||
"image": "/images/article-covers/generating-files.jpg",
|
||||
"image": "images/article-covers/generating-files.jpg",
|
||||
"published": "2020-01-28",
|
||||
}
|
||||
---
|
||||
|
@ -4,7 +4,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"title": "Introducing elm-pages 🚀 - a type-centric static site generator",
|
||||
"description": "Elm is the perfect fit for a static site generator. Learn about some of the features and philosophy behind elm-pages.",
|
||||
"image": "/images/article-covers/introducing-elm-pages.jpg",
|
||||
"image": "images/article-covers/introducing-elm-pages.jpg",
|
||||
"published": "2019-09-24",
|
||||
}
|
||||
---
|
||||
|
@ -4,7 +4,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"title": "A is for API - Introducing Static HTTP Requests",
|
||||
"description": "",
|
||||
"image": "/images/article-covers/static-http.jpg",
|
||||
"image": "images/article-covers/static-http.jpg",
|
||||
"published": "2019-12-10",
|
||||
}
|
||||
---
|
||||
@ -85,7 +85,7 @@ head : Int -> List (Head.Tag Pages.PathKey)
|
||||
head starCount =
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages - "
|
||||
, siteName = "elm-pages - "
|
||||
++ String.fromInt starCount
|
||||
++ " GitHub Stars"
|
||||
, image =
|
||||
@ -197,4 +197,4 @@ You can [take a look at this an end-to-end example app that uses the new `Static
|
||||
|
||||
Or just use the [`elm-pages-starter` repo](https://github.com/dillonkearns/elm-pages-starter) and start building something cool! Let me know your thoughts on Slack, I'd love to hear from you! Or continue the conversation on Twitter!
|
||||
|
||||
<Oembed url="https://twitter.com/dillontkearns/status/1214238507163471872" />
|
||||
<Oembed url="https://twitter.com/dillontkearns/status/1214238507163471872" />
|
||||
|
@ -4,7 +4,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"title": "Types Over Conventions",
|
||||
"description": "How elm-pages approaches configuration, using type-safe Elm.",
|
||||
"image": "/images/article-covers/introducing-elm-pages.jpg",
|
||||
"image": "images/article-covers/introducing-elm-pages.jpg",
|
||||
"draft": true,
|
||||
"published": "2019-09-21",
|
||||
}
|
||||
@ -164,6 +164,6 @@ Now, in our `elm-pages` app, our `view` function will get the markdown that we r
|
||||
## Takeaways
|
||||
So which is better, configuration through types or configuration by convention?
|
||||
|
||||
They both have their benefits. If you're like me, then you enjoy being able to figure out what your Elm code is doing by just following the types. And I hope you'll agree that `elm-pages` gives you that experience for wiring up your content and your parsers.
|
||||
They both have their benefits. If you're like me, then you enjoy being able to figure out what your Elm code is doing by just following the types. And I hope you'll agree that `elm-pages` gives you that experience for wiring up your content and your parsers.
|
||||
|
||||
And when you need to do something more advanced, you've got all the typed data right there and you're empowered to solve the problem using Elm!
|
||||
And when you need to do something more advanced, you've got all the typed data right there and you're empowered to solve the problem using Elm!
|
||||
|
5
examples/docs/package-lock.json
generated
5
examples/docs/package-lock.json
generated
@ -260,6 +260,7 @@
|
||||
"chokidar": "^2.1.5",
|
||||
"closure-webpack-plugin": "^2.0.1",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"cross-spawn": "6.0.5",
|
||||
"css-loader": "^3.2.0",
|
||||
"elm": "^0.19.1-3",
|
||||
"elm-hot-webpack-loader": "^1.1.2",
|
||||
@ -267,18 +268,20 @@
|
||||
"express": "^4.17.1",
|
||||
"favicons-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^4.2.0",
|
||||
"find-elm-dependencies": "2.0.2",
|
||||
"globby": "^10.0.1",
|
||||
"google-closure-compiler": "^20190909.0.0",
|
||||
"gray-matter": "^4.0.2",
|
||||
"html-webpack-plugin": "^4.0.0-beta.11",
|
||||
"imagemin-mozjpeg": "^8.0.0",
|
||||
"imagemin-webpack-plugin": "^2.4.2",
|
||||
"node-elm-compiler": "^5.0.4",
|
||||
"lodash": "4.17.15",
|
||||
"node-sass": "^4.12.0",
|
||||
"prerender-spa-plugin": "^3.4.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"script-ext-html-webpack-plugin": "^2.1.4",
|
||||
"style-loader": "^1.0.0",
|
||||
"temp": "^0.9.0",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-dev-middleware": "^3.7.0",
|
||||
"webpack-hot-middleware": "^2.25.0",
|
||||
|
@ -111,9 +111,6 @@ imageDecoder =
|
||||
|
||||
findMatchingImage : String -> Maybe (ImagePath Pages.PathKey)
|
||||
findMatchingImage imageAssetPath =
|
||||
Pages.allImages
|
||||
|> List.Extra.find
|
||||
(\image ->
|
||||
ImagePath.toString image
|
||||
== imageAssetPath
|
||||
)
|
||||
List.Extra.find
|
||||
(\image -> ImagePath.toString image == imageAssetPath)
|
||||
Pages.allImages
|
||||
|
3
examples/external-data/content/other-page.md
Normal file
3
examples/external-data/content/other-page.md
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: "Hello!"
|
||||
---
|
@ -2,7 +2,7 @@ import "elm-oembed";
|
||||
import "./style.css";
|
||||
// @ts-ignore
|
||||
const { Elm } = require("./src/Main.elm");
|
||||
const pagesInit = require("elm-pages");
|
||||
const pagesInit = require("../../index.js");
|
||||
|
||||
pagesInit({
|
||||
mainElmModule: Elm.Main
|
||||
|
477
examples/external-data/package-lock.json
generated
477
examples/external-data/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -185,7 +185,7 @@ view siteMetadata page =
|
||||
\model viewForPage ->
|
||||
{ title = "Landing Page"
|
||||
, body =
|
||||
[ header starCount
|
||||
[ header page starCount
|
||||
, Element.text "Built at: "
|
||||
, Element.text <| String.fromInt <| Time.posixToMillis Pages.builtAt
|
||||
, pokemon
|
||||
@ -234,8 +234,8 @@ articleImageView articleImage =
|
||||
}
|
||||
|
||||
|
||||
header : Int -> Element msg
|
||||
header starCount =
|
||||
header : { path : PagePath Pages.PathKey, frontmatter : Metadata } -> Int -> Element msg
|
||||
header currentPage starCount =
|
||||
Element.column [ Element.width Element.fill ]
|
||||
[ Element.el
|
||||
[ Element.height (Element.px 4)
|
||||
@ -258,7 +258,12 @@ header starCount =
|
||||
, Element.Border.color (Element.rgba255 40 80 40 0.4)
|
||||
]
|
||||
[ Element.link []
|
||||
{ url = "/"
|
||||
{ url =
|
||||
if currentPage.path == pages.index then
|
||||
PagePath.toString pages.otherPage
|
||||
|
||||
else
|
||||
PagePath.toString pages.index
|
||||
, label =
|
||||
Element.row
|
||||
[ Font.size 30
|
||||
|
@ -36,11 +36,10 @@ prerenderRcFormattedPath : String -> String
|
||||
prerenderRcFormattedPath path =
|
||||
path
|
||||
|> dropExtension
|
||||
|> chopForwardSlashes
|
||||
|> String.split "/"
|
||||
|> dropIndexFromLast
|
||||
|> List.drop 1
|
||||
|> String.join "/"
|
||||
|> (\pathSoFar -> "/" ++ pathSoFar)
|
||||
|
||||
|
||||
dropIndexFromLast : List String -> List String
|
||||
@ -50,7 +49,7 @@ dropIndexFromLast path =
|
||||
|> (\reversePath ->
|
||||
case List.head reversePath of
|
||||
Just "index" ->
|
||||
reversePath |> List.drop 1
|
||||
List.drop 1 reversePath
|
||||
|
||||
_ ->
|
||||
reversePath
|
||||
@ -58,12 +57,17 @@ dropIndexFromLast path =
|
||||
|> List.reverse
|
||||
|
||||
|
||||
chopForwardSlashes : String -> String
|
||||
chopForwardSlashes =
|
||||
String.split "/" >> List.filter ((/=) "") >> String.join "/"
|
||||
|
||||
|
||||
pathFor : { entry | path : String } -> String
|
||||
pathFor page =
|
||||
page.path
|
||||
|> dropExtension
|
||||
|> chopForwardSlashes
|
||||
|> String.split "/"
|
||||
|> List.drop 1
|
||||
|> dropIndexFromLast
|
||||
|> List.map (\pathPart -> String.concat [ "\"", pathPart, "\"" ])
|
||||
|> String.join ", "
|
||||
@ -72,11 +76,11 @@ pathFor page =
|
||||
|
||||
dropExtension : String -> String
|
||||
dropExtension path =
|
||||
if path |> String.endsWith ".emu" then
|
||||
path |> String.dropRight 4
|
||||
if String.endsWith ".emu" path then
|
||||
String.dropRight 4 path
|
||||
|
||||
else if path |> String.endsWith ".md" then
|
||||
path |> String.dropRight 3
|
||||
else if String.endsWith ".md" path then
|
||||
String.dropRight 3 path
|
||||
|
||||
else
|
||||
path
|
||||
@ -219,14 +223,13 @@ init flags cliOptions =
|
||||
|
||||
|
||||
generateFileContents : List MarkdownContent -> List ( String, String )
|
||||
generateFileContents markdownFiles =
|
||||
markdownFiles
|
||||
|> List.map
|
||||
(\file ->
|
||||
( prerenderRcFormattedPath file.path |> String.dropLeft 1
|
||||
, file.body
|
||||
)
|
||||
generateFileContents =
|
||||
List.map
|
||||
(\file ->
|
||||
( prerenderRcFormattedPath file.path
|
||||
, file.body
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
main : Program.StatelessProgram Never Extras
|
||||
|
@ -35,10 +35,7 @@ module.exports = class AddFilesPlugin {
|
||||
// but I found the example code for it here:
|
||||
// https://github.com/jantimon/html-webpack-plugin/blob/35a154186501fba3ecddb819b6f632556d37a58f/index.js#L470-L478
|
||||
|
||||
let route = `/${file.baseRoute}`.replace(/\/$/, '');
|
||||
if (route === '') {
|
||||
route = '/';
|
||||
}
|
||||
let route = file.baseRoute.replace(/\/$/, '');
|
||||
const staticRequests = this.pagesWithRequests[route];
|
||||
|
||||
const filename = path.join(file.baseRoute, "content.json");
|
||||
|
@ -31,8 +31,7 @@ function start({ routes, debug, customPort, manifestConfig, routesWithRequests,
|
||||
hot: true,
|
||||
inline: true,
|
||||
host: "localhost",
|
||||
stats: "errors-only",
|
||||
publicPath: "/"
|
||||
stats: "errors-only"
|
||||
};
|
||||
|
||||
const app = express();
|
||||
@ -49,12 +48,20 @@ function start({ routes, debug, customPort, manifestConfig, routesWithRequests,
|
||||
// don't know why this works, but it does
|
||||
// see: https://github.com/jantimon/html-webpack-plugin/issues/145#issuecomment-170554832
|
||||
const filename = path.join(compiler.outputPath, "index.html");
|
||||
const route = req.originalUrl.replace(/(\w)\/$/, "$1").replace(/^\//, "");
|
||||
const isPage = routes.includes(route);
|
||||
|
||||
compiler.outputFileSystem.readFile(filename, function(err, result) {
|
||||
const contents = isPage
|
||||
? replaceBaseAndLinks(result.toString(), route)
|
||||
: result
|
||||
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.set("content-type", "text/html");
|
||||
res.send(result);
|
||||
res.send(contents);
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
@ -167,9 +174,15 @@ function webpackOptions(
|
||||
defaultAttribute: 'defer'
|
||||
}),
|
||||
new FaviconsWebpackPlugin({
|
||||
logo: path.resolve(process.cwd(), `./${manifestConfig.sourceIcon}`),
|
||||
logo: `./${manifestConfig.sourceIcon}`,
|
||||
prefix: "assets/",
|
||||
|
||||
publicPath: "",
|
||||
outputPath: "",
|
||||
|
||||
favicons: {
|
||||
path: "/", // Path for overriding default icons path. `string`
|
||||
manifestRelativePaths: true,
|
||||
path: "", // Path for overriding default icons path. `string`
|
||||
appName: manifestConfig.name, // Your application's name. `string`
|
||||
appShortName: manifestConfig.short_name, // Your application's short_name. `string`. Optional. If not set, appName will be used
|
||||
appDescription: manifestConfig.description, // Your application's description. `string`
|
||||
@ -226,9 +239,7 @@ function webpackOptions(
|
||||
// (drag-and-drop `events.json` file into Chrome performance tab)
|
||||
// , new webpack.debug.ProfilingPlugin()
|
||||
],
|
||||
output: {
|
||||
publicPath: "/"
|
||||
},
|
||||
output: {},
|
||||
resolve: {
|
||||
modules: [
|
||||
path.resolve(process.cwd(), `./node_modules`),
|
||||
@ -307,10 +318,22 @@ function webpackOptions(
|
||||
}),
|
||||
new PrerenderSPAPlugin({
|
||||
staticDir: path.join(process.cwd(), "dist"),
|
||||
routes: routes,
|
||||
routes: routes.map(r => `/${r}`),
|
||||
|
||||
renderer: new PrerenderSPAPlugin.PuppeteerRenderer({
|
||||
renderAfterDocumentEvent: "prerender-trigger",
|
||||
})
|
||||
headless: true,
|
||||
devtools: false
|
||||
}),
|
||||
|
||||
postProcess: renderedRoute => {
|
||||
renderedRoute.html = replaceBaseAndLinks(
|
||||
renderedRoute.html,
|
||||
renderedRoute.route
|
||||
)
|
||||
|
||||
return renderedRoute
|
||||
}
|
||||
})
|
||||
],
|
||||
module: {
|
||||
@ -361,3 +384,27 @@ function webpackOptions(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cleanRoute(route) {
|
||||
return route.replace(/(^\/|\/$)/, "")
|
||||
}
|
||||
|
||||
|
||||
function pathToRoot(cleanedRoute) {
|
||||
return cleanedRoute === ""
|
||||
? cleanedRoute
|
||||
: cleanedRoute
|
||||
.split("/")
|
||||
.map(_ => "..")
|
||||
.join("/")
|
||||
.replace(/\.$/, "./")
|
||||
}
|
||||
|
||||
|
||||
function replaceBaseAndLinks(html, route) {
|
||||
const cleanedRoute = cleanRoute(route)
|
||||
|
||||
const href = cleanedRoute === '' ? './' : pathToRoot(cleanedRoute)
|
||||
return (html || "").replace(`<base href="/"`, `<base href="${href}"`)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ ${staticRoutes.routeRecord}
|
||||
|
||||
${staticRoutes.imageAssetsRecord}
|
||||
|
||||
|
||||
allImages : List (ImagePath PathKey)
|
||||
allImages =
|
||||
[${staticRoutes.allImages.join("\n , ")}
|
||||
@ -64,6 +65,7 @@ builtAt : Time.Posix
|
||||
builtAt =
|
||||
Time.millisToPosix ${Math.round((global.builtAt).getTime())}
|
||||
|
||||
|
||||
type PathKey
|
||||
= PathKey
|
||||
|
||||
@ -73,7 +75,6 @@ buildImage path =
|
||||
ImagePath.build PathKey ("images" :: path)
|
||||
|
||||
|
||||
|
||||
buildPage : List String -> PagePath PathKey
|
||||
buildPage path =
|
||||
PagePath.build PathKey path
|
||||
@ -99,7 +100,7 @@ internals =
|
||||
, content = content
|
||||
, pathKey = PathKey
|
||||
}
|
||||
|
||||
|
||||
${staticRouteStuff(staticRoutes)}
|
||||
|
||||
${generateRawContent(markdownContent, markupContent, false)}
|
||||
@ -130,6 +131,7 @@ builtAt : Time.Posix
|
||||
builtAt =
|
||||
Time.millisToPosix ${Math.round((global.builtAt).getTime())}
|
||||
|
||||
|
||||
type PathKey
|
||||
= PathKey
|
||||
|
||||
|
@ -54,6 +54,7 @@ function run() {
|
||||
.map(({ path, contents }) => {
|
||||
return parseMarkdown(path, contents);
|
||||
});
|
||||
|
||||
const images = globby
|
||||
.sync("images/**/*", {})
|
||||
.filter(imagePath => !fs.lstatSync(imagePath).isDirectory());
|
||||
@ -117,7 +118,7 @@ function run() {
|
||||
}
|
||||
|
||||
ensureDirSync("./gen");
|
||||
|
||||
|
||||
// prevent compilation errors if migrating from previous elm-pages version
|
||||
deleteIfExists("./gen/Pages/ContentCache.elm");
|
||||
deleteIfExists("./gen/Pages/Platform.elm");
|
||||
@ -169,7 +170,8 @@ function toRoute(entry) {
|
||||
let fullPath = entry.path
|
||||
.replace(/(index)?\.[^/.]+$/, "")
|
||||
.split("/")
|
||||
.filter(item => item !== "");
|
||||
fullPath.splice(0, 1);
|
||||
return `/${fullPath.join("/")}`;
|
||||
.filter(item => item !== "")
|
||||
.slice(1);
|
||||
|
||||
return fullPath.join("/");
|
||||
}
|
||||
|
@ -26,11 +26,16 @@ function toEntry(entry, includeBody) {
|
||||
`;
|
||||
}
|
||||
|
||||
function multilineElmString(string) {
|
||||
const escapedString = string
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"""/g, '\\"\\"\\"');
|
||||
return `"""${escapedString}"""`;
|
||||
}
|
||||
|
||||
function body(entry, includeBody) {
|
||||
if (includeBody) {
|
||||
return `Just """${entry.body.replace(/\\/g, "\\\\")}
|
||||
"""
|
||||
`;
|
||||
return `Just ${multilineElmString(entry.body)}`;
|
||||
} else {
|
||||
return `Nothing`;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ const glob = require("glob");
|
||||
const fs = require("fs");
|
||||
const parseFrontmatter = require("./frontmatter.js");
|
||||
|
||||
// Because we use node-glob, we must use `/` as a separator on all platforms. See https://github.com/isaacs/node-glob#windows
|
||||
const PATH_SEPARATOR = '/';
|
||||
|
||||
module.exports = function wrapper() {
|
||||
return generate(scan());
|
||||
};
|
||||
@ -41,9 +44,9 @@ function unpackFile() {
|
||||
function relativeImagePath(imageFilepath) {
|
||||
var pathFragments = imageFilepath;
|
||||
//remove extesion and split into fragments
|
||||
const fragmentsWithExtension = pathFragments.split(path.sep);
|
||||
const fragmentsWithExtension = pathFragments.split(PATH_SEPARATOR);
|
||||
fragmentsWithExtension.splice(0, 1);
|
||||
pathFragments = pathFragments.replace(/\.[^/.]+$/, "").split(path.sep);
|
||||
pathFragments = pathFragments.replace(/\.[^/.]+$/, "").split(PATH_SEPARATOR);
|
||||
pathFragments.splice(0, 1);
|
||||
const fullPath = imageFilepath;
|
||||
var relative = imageFilepath.slice(dir.length - 1);
|
||||
@ -69,7 +72,7 @@ function generate(scanned) {
|
||||
for (var i = 0; i < scanned.length; i++) {
|
||||
var pathFragments = scanned[i].path;
|
||||
//remove extesion and split into fragments
|
||||
pathFragments = pathFragments.replace(/\.[^/.]+$/, "").split(path.sep);
|
||||
pathFragments = pathFragments.replace(/\.[^/.]+$/, "").split(PATH_SEPARATOR);
|
||||
const is404 = pathFragments.length == 1 && pathFragments[0] == "404";
|
||||
const ext = path.extname(scanned[i].path);
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="preload" href="./content.json" as="fetch" crossorigin />
|
||||
<link rel="preload" href="content.json" as="fetch" crossorigin />
|
||||
|
||||
<base href="/" />
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<script>
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker.register("/service-worker.js");
|
||||
navigator.serviceWorker.register("service-worker.js");
|
||||
});
|
||||
} else {
|
||||
console.log("No service worker registered.");
|
||||
|
31
index.js
31
index.js
@ -13,12 +13,15 @@ module.exports = function pagesInit(
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
document.addEventListener("DOMContentLoaded", _ => {
|
||||
new MutationObserver(function() {
|
||||
elmViewRendered = true;
|
||||
if (headTagsAdded) {
|
||||
document.dispatchEvent(new Event("prerender-trigger"));
|
||||
}
|
||||
}).observe(document.body, { attributes: true, childList: true, subtree: true});
|
||||
new MutationObserver(function() {
|
||||
elmViewRendered = true;
|
||||
if (headTagsAdded) {
|
||||
document.dispatchEvent(new Event("prerender-trigger"));
|
||||
}
|
||||
}).observe(
|
||||
document.body,
|
||||
{ attributes: true, childList: true, subtree: true }
|
||||
);
|
||||
|
||||
loadContentAndInitializeApp(mainElmModule).then(resolve, reject);
|
||||
});
|
||||
@ -26,12 +29,18 @@ module.exports = function pagesInit(
|
||||
};
|
||||
|
||||
function loadContentAndInitializeApp(/** @type { init: any } */ mainElmModule) {
|
||||
return httpGet(`${window.location.origin}${window.location.pathname}/content.json`).then(function(/** @type JSON */ contentJson) {
|
||||
const isPrerendering = navigator.userAgent.indexOf("Headless") >= 0
|
||||
const path = window.location.pathname.replace(/(\w)$/, "$1/")
|
||||
|
||||
return httpGet(`${window.location.origin}${path}content.json`).then(function(/** @type JSON */ contentJson) {
|
||||
|
||||
const app = mainElmModule.init({
|
||||
flags: {
|
||||
secrets: null,
|
||||
isPrerendering: navigator.userAgent.indexOf("Headless") >= 0,
|
||||
baseUrl: isPrerendering
|
||||
? window.location.origin
|
||||
: document.baseURI,
|
||||
isPrerendering: isPrerendering,
|
||||
contentJson
|
||||
}
|
||||
});
|
||||
@ -46,8 +55,8 @@ function loadContentAndInitializeApp(/** @type { init: any } */ mainElmModule)
|
||||
["content", `elm-pages v${elmPagesVersion}`]
|
||||
]
|
||||
});
|
||||
window.allRoutes = fromElm.allRoutes;
|
||||
|
||||
|
||||
window.allRoutes = fromElm.allRoutes.map(route => new URL(route, document.baseURI).href);
|
||||
|
||||
if (navigator.userAgent.indexOf("Headless") >= 0) {
|
||||
fromElm.head.forEach(headTag => {
|
||||
@ -141,8 +150,8 @@ function prefetchIfNeeded(/** @type {HTMLAnchorElement} */ target) {
|
||||
if (target.host === window.location.host) {
|
||||
if (prefetchedPages.includes(target.pathname)) {
|
||||
// console.log("Already preloaded", target.href);
|
||||
} else if (!allRoutes.includes(target.pathname)) {
|
||||
// console.log("Not a known route, skipping preload", target.pathname);
|
||||
} else if (!allRoutes.includes(new URL(target.pathname, document.baseURI).href)) {
|
||||
}
|
||||
else {
|
||||
prefetchedPages.push(target.pathname);
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "elm-pages",
|
||||
"version": "1.2.10",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "elm-pages",
|
||||
"version": "1.2.10",
|
||||
"version": "1.3.0",
|
||||
"homepage": "http://elm-pages.com",
|
||||
"description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
|
||||
"main": "index.js",
|
||||
|
@ -33,6 +33,7 @@ writing a plugin package to extend `elm-pages`.
|
||||
|
||||
import Json.Encode
|
||||
import Pages.ImagePath as ImagePath exposing (ImagePath)
|
||||
import Pages.Internal.String as String
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
|
||||
|
||||
@ -217,8 +218,4 @@ encodeProperty canonicalSiteUrl currentPagePath ( name, value ) =
|
||||
|
||||
joinPaths : String -> String -> String
|
||||
joinPaths base path =
|
||||
if (base |> String.endsWith "/") && (path |> String.startsWith "/") then
|
||||
base ++ String.dropLeft 1 path
|
||||
|
||||
else
|
||||
base ++ path
|
||||
String.chopEnd "/" base ++ "/" ++ String.chopStart "/" path
|
||||
|
@ -24,6 +24,7 @@ import Json.Decode as Decode
|
||||
import Mark
|
||||
import Mark.Error
|
||||
import Pages.Document as Document exposing (Document)
|
||||
import Pages.Internal.String as String
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Result.Extra
|
||||
import Task exposing (Task)
|
||||
@ -88,22 +89,21 @@ pagesWithErrors cache =
|
||||
cache
|
||||
|> Result.map
|
||||
(\okCache ->
|
||||
okCache
|
||||
|> Dict.toList
|
||||
|> List.filterMap
|
||||
(\( path, value ) ->
|
||||
case value of
|
||||
Parsed metadata { body } ->
|
||||
case body of
|
||||
Err parseError ->
|
||||
createBuildError path parseError |> Just
|
||||
List.filterMap
|
||||
(\( path, value ) ->
|
||||
case value of
|
||||
Parsed metadata { body } ->
|
||||
case body of
|
||||
Err parseError ->
|
||||
createBuildError path parseError |> Just
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
(Dict.toList okCache)
|
||||
)
|
||||
|> Result.withDefault []
|
||||
|
||||
@ -114,22 +114,17 @@ init :
|
||||
-> Maybe { contentJson : ContentJson String, initialUrl : Url }
|
||||
-> ContentCache metadata view
|
||||
init document content maybeInitialPageContent =
|
||||
parseMetadata maybeInitialPageContent document content
|
||||
content
|
||||
|> parseMetadata maybeInitialPageContent document
|
||||
|> List.map
|
||||
(\tuple ->
|
||||
Tuple.mapSecond
|
||||
(\result ->
|
||||
result
|
||||
|> Result.mapError
|
||||
(\error ->
|
||||
-- ( Tuple.first tuple, error )
|
||||
createErrors (Tuple.first tuple) error
|
||||
)
|
||||
)
|
||||
tuple
|
||||
tuple
|
||||
|> Tuple.first
|
||||
|> createErrors
|
||||
|> Result.mapError
|
||||
|> (\f -> Tuple.mapSecond f tuple)
|
||||
)
|
||||
|> combineTupleResults
|
||||
-- |> Result.mapError Dict.fromList
|
||||
|> Result.map Dict.fromList
|
||||
|
||||
|
||||
@ -142,7 +137,7 @@ createBuildError path decodeError =
|
||||
{ title = "Metadata Decode Error"
|
||||
, message =
|
||||
[ Terminal.text "I ran into a problem when parsing the metadata for the page with this path: "
|
||||
, Terminal.text ("/" ++ (path |> String.join "/"))
|
||||
, Terminal.text (String.join "/" path)
|
||||
, Terminal.text "\n\n"
|
||||
, Terminal.text decodeError
|
||||
]
|
||||
@ -156,44 +151,43 @@ parseMetadata :
|
||||
-> List ( List String, { extension : String, frontMatter : String, body : Maybe String } )
|
||||
-> List ( List String, Result String (Entry metadata view) )
|
||||
parseMetadata maybeInitialPageContent document content =
|
||||
content
|
||||
|> List.map
|
||||
(\( path, { frontMatter, extension, body } ) ->
|
||||
let
|
||||
maybeDocumentEntry =
|
||||
Document.get extension document
|
||||
in
|
||||
case maybeDocumentEntry of
|
||||
Just documentEntry ->
|
||||
frontMatter
|
||||
|> documentEntry.frontmatterParser
|
||||
|> Result.map
|
||||
(\metadata ->
|
||||
let
|
||||
renderer =
|
||||
\value ->
|
||||
parseContent extension value document
|
||||
in
|
||||
case maybeInitialPageContent of
|
||||
Just { contentJson, initialUrl } ->
|
||||
if normalizePath initialUrl.path == (String.join "/" path |> normalizePath) then
|
||||
Parsed metadata
|
||||
{ body = renderer contentJson.body
|
||||
, staticData = contentJson.staticData
|
||||
}
|
||||
List.map
|
||||
(\( path, { frontMatter, extension, body } ) ->
|
||||
let
|
||||
maybeDocumentEntry =
|
||||
Document.get extension document
|
||||
in
|
||||
case maybeDocumentEntry of
|
||||
Just documentEntry ->
|
||||
frontMatter
|
||||
|> documentEntry.frontmatterParser
|
||||
|> Result.map
|
||||
(\metadata ->
|
||||
let
|
||||
renderer value =
|
||||
parseContent extension value document
|
||||
in
|
||||
case maybeInitialPageContent of
|
||||
Just { contentJson, initialUrl } ->
|
||||
if normalizePath initialUrl.path == (String.join "/" path |> normalizePath) then
|
||||
Parsed metadata
|
||||
{ body = renderer contentJson.body
|
||||
, staticData = contentJson.staticData
|
||||
}
|
||||
|
||||
else
|
||||
NeedContent extension metadata
|
||||
|
||||
Nothing ->
|
||||
else
|
||||
NeedContent extension metadata
|
||||
)
|
||||
|> Tuple.pair path
|
||||
|
||||
Nothing ->
|
||||
Err ("Could not find extension '" ++ extension ++ "'")
|
||||
|> Tuple.pair path
|
||||
)
|
||||
Nothing ->
|
||||
NeedContent extension metadata
|
||||
)
|
||||
|> Tuple.pair path
|
||||
|
||||
Nothing ->
|
||||
Err ("Could not find extension '" ++ extension ++ "'")
|
||||
|> Tuple.pair path
|
||||
)
|
||||
content
|
||||
|
||||
|
||||
normalizePath : String -> String
|
||||
@ -206,16 +200,15 @@ normalizePath pathString =
|
||||
String.endsWith "/" pathString
|
||||
in
|
||||
if pathString == "" then
|
||||
"/"
|
||||
pathString
|
||||
|
||||
else
|
||||
String.concat
|
||||
[ if hasPrefix then
|
||||
""
|
||||
String.dropLeft 1 pathString
|
||||
|
||||
else
|
||||
"/"
|
||||
, pathString
|
||||
pathString
|
||||
, if hasSuffix then
|
||||
""
|
||||
|
||||
@ -257,7 +250,7 @@ createHtmlError : List String -> String -> Html msg
|
||||
createHtmlError path error =
|
||||
Html.div []
|
||||
[ Html.h2 []
|
||||
[ Html.text ("/" ++ (path |> String.join "/"))
|
||||
[ Html.text (String.join "/" path)
|
||||
]
|
||||
, Html.p [] [ Html.text "I couldn't parse the frontmatter in this page. I ran into this error with your JSON decoder:" ]
|
||||
, Html.pre [] [ Html.text error ]
|
||||
@ -269,7 +262,6 @@ routes record =
|
||||
record
|
||||
|> List.map Tuple.first
|
||||
|> List.map (String.join "/")
|
||||
|> List.map (\route -> "/" ++ route)
|
||||
|
||||
|
||||
routesForCache : ContentCache metadata view -> List String
|
||||
@ -294,7 +286,7 @@ type alias Page metadata view pathKey =
|
||||
renderErrors : ( List String, List Mark.Error.Error ) -> Html msg
|
||||
renderErrors ( path, errors ) =
|
||||
Html.div []
|
||||
[ Html.text (path |> String.join "/")
|
||||
[ Html.text (String.join "/" path)
|
||||
, errors
|
||||
|> List.map (Mark.Error.toHtml Mark.Error.Light)
|
||||
|> Html.div []
|
||||
@ -349,37 +341,40 @@ parse it before returning it and store the parsed version in the Cache
|
||||
-}
|
||||
lazyLoad :
|
||||
Document metadata view
|
||||
-> Url
|
||||
-> { currentUrl : Url, baseUrl : Url }
|
||||
-> ContentCache metadata view
|
||||
-> Task Http.Error (ContentCache metadata view)
|
||||
lazyLoad document url cacheResult =
|
||||
lazyLoad document urls cacheResult =
|
||||
case cacheResult of
|
||||
Err _ ->
|
||||
Task.succeed cacheResult
|
||||
|
||||
Ok cache ->
|
||||
case Dict.get (pathForUrl url) cache of
|
||||
case Dict.get (pathForUrl urls) cache of
|
||||
Just entry ->
|
||||
case entry of
|
||||
NeedContent extension _ ->
|
||||
httpTask url
|
||||
urls.currentUrl
|
||||
|> httpTask
|
||||
|> Task.map
|
||||
(\downloadedContent ->
|
||||
update cacheResult
|
||||
update
|
||||
cacheResult
|
||||
(\value ->
|
||||
parseContent extension value document
|
||||
)
|
||||
url
|
||||
urls
|
||||
downloadedContent
|
||||
)
|
||||
|
||||
Unparsed extension metadata content ->
|
||||
update cacheResult
|
||||
(\thing ->
|
||||
parseContent extension thing document
|
||||
)
|
||||
url
|
||||
content
|
||||
content
|
||||
|> update
|
||||
cacheResult
|
||||
(\thing ->
|
||||
parseContent extension thing document
|
||||
)
|
||||
urls
|
||||
|> Task.succeed
|
||||
|
||||
Parsed _ _ ->
|
||||
@ -395,12 +390,13 @@ httpTask url =
|
||||
{ method = "GET"
|
||||
, headers = []
|
||||
, url =
|
||||
Url.Builder.absolute
|
||||
((url.path |> String.split "/" |> List.filter (not << String.isEmpty))
|
||||
++ [ "content.json"
|
||||
]
|
||||
)
|
||||
[]
|
||||
url.path
|
||||
|> String.chopForwardSlashes
|
||||
|> String.split "/"
|
||||
|> List.filter ((/=) "")
|
||||
|> (\l -> l ++ [ "content.json" ])
|
||||
|> String.join "/"
|
||||
|> String.append "/"
|
||||
, body = Http.emptyBody
|
||||
, resolver =
|
||||
Http.stringResolver
|
||||
@ -443,13 +439,14 @@ contentJsonDecoder =
|
||||
update :
|
||||
ContentCache metadata view
|
||||
-> (String -> Result ParseError view)
|
||||
-> Url
|
||||
-> { currentUrl : Url, baseUrl : Url }
|
||||
-> ContentJson String
|
||||
-> ContentCache metadata view
|
||||
update cacheResult renderer url rawContent =
|
||||
update cacheResult renderer urls rawContent =
|
||||
case cacheResult of
|
||||
Ok cache ->
|
||||
Dict.update (pathForUrl url)
|
||||
Dict.update
|
||||
(pathForUrl urls)
|
||||
(\entry ->
|
||||
case entry of
|
||||
Just (Parsed metadata view) ->
|
||||
@ -482,27 +479,29 @@ update cacheResult renderer url rawContent =
|
||||
Err error
|
||||
|
||||
|
||||
pathForUrl : Url -> Path
|
||||
pathForUrl url =
|
||||
url.path
|
||||
|> dropTrailingSlash
|
||||
pathForUrl : { currentUrl : Url, baseUrl : Url } -> Path
|
||||
pathForUrl { currentUrl, baseUrl } =
|
||||
currentUrl.path
|
||||
|> String.dropLeft (String.length baseUrl.path)
|
||||
|> String.chopForwardSlashes
|
||||
|> String.split "/"
|
||||
|> List.drop 1
|
||||
|> List.filter ((/=) "")
|
||||
|
||||
|
||||
lookup :
|
||||
pathKey
|
||||
-> ContentCache metadata view
|
||||
-> Url
|
||||
-> { currentUrl : Url, baseUrl : Url }
|
||||
-> Maybe ( PagePath pathKey, Entry metadata view )
|
||||
lookup pathKey content url =
|
||||
lookup pathKey content urls =
|
||||
case content of
|
||||
Ok dict ->
|
||||
let
|
||||
path =
|
||||
pathForUrl url
|
||||
pathForUrl urls
|
||||
in
|
||||
Dict.get path dict
|
||||
dict
|
||||
|> Dict.get path
|
||||
|> Maybe.map
|
||||
(\entry ->
|
||||
( PagePath.build pathKey path, entry )
|
||||
@ -515,10 +514,11 @@ lookup pathKey content url =
|
||||
lookupMetadata :
|
||||
pathKey
|
||||
-> ContentCache metadata view
|
||||
-> Url
|
||||
-> { currentUrl : Url, baseUrl : Url }
|
||||
-> Maybe ( PagePath pathKey, metadata )
|
||||
lookupMetadata pathKey content url =
|
||||
lookup pathKey content url
|
||||
lookupMetadata pathKey content urls =
|
||||
urls
|
||||
|> lookup pathKey content
|
||||
|> Maybe.map
|
||||
(\( pagePath, entry ) ->
|
||||
case entry of
|
||||
@ -531,11 +531,3 @@ lookupMetadata pathKey content url =
|
||||
Parsed metadata _ ->
|
||||
( pagePath, metadata )
|
||||
)
|
||||
|
||||
|
||||
dropTrailingSlash path =
|
||||
if path |> String.endsWith "/" then
|
||||
String.dropRight 1 path
|
||||
|
||||
else
|
||||
path
|
||||
|
@ -116,7 +116,7 @@ includes (Directory key allPagePaths directoryPath) pagePath =
|
||||
Pages.pages.blog.directory
|
||||
|
||||
-- blogDirectory |> Directory.indexPath |> PagePath.toString
|
||||
-- => "/blog"
|
||||
-- => "blog"
|
||||
|
||||
See `Directory.includes` for an example of this in action.
|
||||
|
||||
@ -141,9 +141,8 @@ basePath (Directory key allPagePaths directoryPath) =
|
||||
|
||||
|
||||
toString : List String -> String
|
||||
toString rawPath =
|
||||
"/"
|
||||
++ (rawPath |> String.join "/")
|
||||
toString =
|
||||
String.join "/"
|
||||
|
||||
|
||||
{-| Used by the generated `Pages.elm` module. There's no need to use this
|
||||
|
@ -25,7 +25,7 @@ This gives you a record, based on all the files in your local
|
||||
Pages.pages.index
|
||||
|
||||
-- ImagePath.toString homePath
|
||||
-- => "/"
|
||||
-- => ""
|
||||
|
||||
or
|
||||
|
||||
@ -37,7 +37,7 @@ or
|
||||
Pages.images.profilePhotos.dillon
|
||||
|
||||
-- ImagePath.toString helloWorldPostPath
|
||||
-- => "/images/profile-photos/dillon.jpg"
|
||||
-- => "images/profile-photos/dillon.jpg"
|
||||
|
||||
@docs ImagePath, toString, external
|
||||
|
||||
@ -65,7 +65,7 @@ type ImagePath key
|
||||
| External String
|
||||
|
||||
|
||||
{-| Gives you the image's absolute URL as a String. This is useful for constructing `<img>` tags:
|
||||
{-| Gives you the image's relative URL as a String. This is useful for constructing `<img>` tags:
|
||||
|
||||
import Html exposing (Html, img)
|
||||
import Html.Attributes exposing (src)
|
||||
@ -87,8 +87,7 @@ toString : ImagePath key -> String
|
||||
toString path =
|
||||
case path of
|
||||
Internal rawPath ->
|
||||
"/"
|
||||
++ (rawPath |> String.join "/")
|
||||
String.join "/" rawPath
|
||||
|
||||
External url ->
|
||||
url
|
||||
|
@ -15,6 +15,7 @@ import Mark
|
||||
import Pages.ContentCache as ContentCache exposing (ContentCache)
|
||||
import Pages.Document
|
||||
import Pages.Internal.Platform.Cli
|
||||
import Pages.Internal.String as String
|
||||
import Pages.Manifest as Manifest
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Pages.StaticHttp as StaticHttp
|
||||
@ -24,14 +25,6 @@ import Task exposing (Task)
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
dropTrailingSlash path =
|
||||
if path |> String.endsWith "/" then
|
||||
String.dropRight 1 path
|
||||
|
||||
else
|
||||
path
|
||||
|
||||
|
||||
type alias Page metadata view pathKey =
|
||||
{ metadata : metadata
|
||||
, path : PagePath pathKey
|
||||
@ -81,12 +74,13 @@ mainView pathKey pageView model =
|
||||
}
|
||||
|
||||
|
||||
urlToPagePath : pathKey -> Url -> PagePath pathKey
|
||||
urlToPagePath pathKey url =
|
||||
urlToPagePath : pathKey -> Url -> Url -> PagePath pathKey
|
||||
urlToPagePath pathKey url baseUrl =
|
||||
url.path
|
||||
|> dropTrailingSlash
|
||||
|> String.dropLeft (String.length baseUrl.path)
|
||||
|> String.chopForwardSlashes
|
||||
|> String.split "/"
|
||||
|> List.drop 1
|
||||
|> List.filter ((/=) "")
|
||||
|> PagePath.build pathKey
|
||||
|
||||
|
||||
@ -108,19 +102,25 @@ pageViewOrError :
|
||||
-> ContentCache metadata view
|
||||
-> { title : String, body : Html userMsg }
|
||||
pageViewOrError pathKey viewFn model cache =
|
||||
case ContentCache.lookup pathKey cache model.url of
|
||||
let
|
||||
urls =
|
||||
{ currentUrl = model.url
|
||||
, baseUrl = model.baseUrl
|
||||
}
|
||||
in
|
||||
case ContentCache.lookup pathKey cache urls of
|
||||
Just ( pagePath, entry ) ->
|
||||
case entry of
|
||||
ContentCache.Parsed metadata viewResult ->
|
||||
let
|
||||
viewFnResult =
|
||||
viewFn
|
||||
(cache
|
||||
|> Result.map (ContentCache.extractMetadata pathKey)
|
||||
|> Result.withDefault []
|
||||
-- TODO handle error better
|
||||
)
|
||||
{ path = pagePath, frontmatter = metadata }
|
||||
{ path = pagePath, frontmatter = metadata }
|
||||
|> viewFn
|
||||
(cache
|
||||
|> Result.map (ContentCache.extractMetadata pathKey)
|
||||
|> Result.withDefault []
|
||||
-- TODO handle error better
|
||||
)
|
||||
|> (\request ->
|
||||
StaticHttpRequest.resolve request viewResult.staticData
|
||||
)
|
||||
@ -263,7 +263,10 @@ init :
|
||||
init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel flags url key =
|
||||
let
|
||||
contentCache =
|
||||
ContentCache.init document content (Maybe.map (\cj -> { contentJson = cj, initialUrl = url }) contentJson)
|
||||
ContentCache.init
|
||||
document
|
||||
content
|
||||
(Maybe.map (\cj -> { contentJson = cj, initialUrl = url }) contentJson)
|
||||
|
||||
contentJson =
|
||||
flags
|
||||
@ -275,6 +278,18 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
|
||||
Decode.map2 ContentJson
|
||||
(Decode.field "body" Decode.string)
|
||||
(Decode.field "staticData" (Decode.dict Decode.string))
|
||||
|
||||
baseUrl =
|
||||
flags
|
||||
|> Decode.decodeValue (Decode.field "baseUrl" Decode.string)
|
||||
|> Result.toMaybe
|
||||
|> Maybe.andThen Url.fromString
|
||||
|> Maybe.withDefault url
|
||||
|
||||
urls =
|
||||
{ currentUrl = url
|
||||
, baseUrl = baseUrl
|
||||
}
|
||||
in
|
||||
case contentCache of
|
||||
Ok okCache ->
|
||||
@ -291,23 +306,24 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
|
||||
Client
|
||||
|
||||
( userModel, userCmd ) =
|
||||
initUserModel
|
||||
(maybePagePath
|
||||
|> Maybe.map
|
||||
(\pagePath ->
|
||||
{ path = pagePath
|
||||
, query = url.query
|
||||
, fragment = url.fragment
|
||||
}
|
||||
)
|
||||
)
|
||||
maybePagePath
|
||||
|> Maybe.map
|
||||
(\pagePath ->
|
||||
{ path = pagePath
|
||||
, query = url.query
|
||||
, fragment = url.fragment
|
||||
}
|
||||
)
|
||||
|> initUserModel
|
||||
|
||||
cmd =
|
||||
case ( maybePagePath, maybeMetadata ) of
|
||||
( Just pagePath, Just frontmatter ) ->
|
||||
[ userCmd |> Cmd.map UserMsg |> Just
|
||||
[ userCmd
|
||||
|> Cmd.map UserMsg
|
||||
|> Just
|
||||
, contentCache
|
||||
|> ContentCache.lazyLoad document url
|
||||
|> ContentCache.lazyLoad document urls
|
||||
|> Task.attempt UpdateCache
|
||||
|> Just
|
||||
]
|
||||
@ -318,7 +334,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
|
||||
Cmd.none
|
||||
|
||||
( maybePagePath, maybeMetadata ) =
|
||||
case ContentCache.lookupMetadata pathKey (Ok okCache) url of
|
||||
case ContentCache.lookupMetadata pathKey (Ok okCache) urls of
|
||||
Just ( pagePath, metadata ) ->
|
||||
( Just pagePath, Just metadata )
|
||||
|
||||
@ -327,6 +343,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
|
||||
in
|
||||
( { key = key
|
||||
, url = url
|
||||
, baseUrl = baseUrl
|
||||
, userModel = userModel
|
||||
, contentCache = contentCache
|
||||
, phase = phase
|
||||
@ -341,6 +358,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
|
||||
in
|
||||
( { key = key
|
||||
, url = url
|
||||
, baseUrl = baseUrl
|
||||
, userModel = userModel
|
||||
, contentCache = contentCache
|
||||
, phase = Client
|
||||
@ -381,7 +399,8 @@ type Model userModel userMsg metadata view
|
||||
|
||||
type alias ModelDetails userModel metadata view =
|
||||
{ key : Browser.Navigation.Key
|
||||
, url : Url.Url
|
||||
, url : Url
|
||||
, baseUrl : Url
|
||||
, contentCache : ContentCache metadata view
|
||||
, userModel : userModel
|
||||
, phase : Phase
|
||||
@ -432,10 +451,7 @@ update allRoutes canonicalSiteUrl viewFunction pathKey maybeOnPageChangeMsg toJs
|
||||
Browser.Internal url ->
|
||||
let
|
||||
navigatingToSamePage =
|
||||
url.path
|
||||
== model.url.path
|
||||
&& url
|
||||
/= model.url
|
||||
(url.path == model.url.path) && (url /= model.url)
|
||||
in
|
||||
if navigatingToSamePage then
|
||||
-- this is a workaround for an issue with anchor fragment navigation
|
||||
@ -451,10 +467,12 @@ update allRoutes canonicalSiteUrl viewFunction pathKey maybeOnPageChangeMsg toJs
|
||||
UrlChanged url ->
|
||||
let
|
||||
navigatingToSamePage =
|
||||
url.path
|
||||
== model.url.path
|
||||
&& url
|
||||
/= model.url
|
||||
(url.path == model.url.path) && (url /= model.url)
|
||||
|
||||
urls =
|
||||
{ currentUrl = url
|
||||
, baseUrl = model.baseUrl
|
||||
}
|
||||
in
|
||||
( model
|
||||
, if navigatingToSamePage then
|
||||
@ -467,7 +485,7 @@ update allRoutes canonicalSiteUrl viewFunction pathKey maybeOnPageChangeMsg toJs
|
||||
|
||||
else
|
||||
model.contentCache
|
||||
|> ContentCache.lazyLoad document url
|
||||
|> ContentCache.lazyLoad document urls
|
||||
|> Task.attempt (UpdateCacheAndUrl url)
|
||||
)
|
||||
|
||||
@ -484,8 +502,13 @@ update allRoutes canonicalSiteUrl viewFunction pathKey maybeOnPageChangeMsg toJs
|
||||
-- to keep track of the last url change
|
||||
Ok updatedCache ->
|
||||
let
|
||||
urls =
|
||||
{ currentUrl = model.url
|
||||
, baseUrl = model.baseUrl
|
||||
}
|
||||
|
||||
maybeCmd =
|
||||
case ContentCache.lookup pathKey updatedCache model.url of
|
||||
case ContentCache.lookup pathKey updatedCache urls of
|
||||
Just ( pagePath, entry ) ->
|
||||
case entry of
|
||||
ContentCache.Parsed frontmatter viewResult ->
|
||||
@ -536,7 +559,7 @@ update allRoutes canonicalSiteUrl viewFunction pathKey maybeOnPageChangeMsg toJs
|
||||
Just onPageChangeMsg ->
|
||||
userUpdate
|
||||
(onPageChangeMsg
|
||||
{ path = url |> urlToPagePath pathKey
|
||||
{ path = urlToPagePath pathKey url model.baseUrl
|
||||
, query = url.query
|
||||
, fragment = url.fragment
|
||||
}
|
||||
@ -666,7 +689,6 @@ application config =
|
||||
config.content
|
||||
|> List.map Tuple.first
|
||||
|> List.map (String.join "/")
|
||||
|> List.map (\route -> "/" ++ route)
|
||||
in
|
||||
update allRoutes config.canonicalSiteUrl config.view config.pathKey config.onPageChange config.toJsPort config.document userUpdate msg model
|
||||
|> Tuple.mapFirst Model
|
||||
|
@ -279,10 +279,10 @@ perform cliMsgConstructor toJsPort effect =
|
||||
|> Cmd.batch
|
||||
|
||||
FetchHttp ({ unmasked, masked } as requests) ->
|
||||
--let
|
||||
-- _ =
|
||||
-- Debug.log "Fetching" masked.url
|
||||
--in
|
||||
-- let
|
||||
-- _ =
|
||||
-- Debug.log "Fetching" masked.url
|
||||
-- in
|
||||
Http.request
|
||||
{ method = unmasked.method
|
||||
, url = unmasked.url
|
||||
@ -329,15 +329,15 @@ init toModel contentCache siteMetadata config flags =
|
||||
Ok ( secrets, mode ) ->
|
||||
case contentCache of
|
||||
Ok _ ->
|
||||
case contentCache |> ContentCache.pagesWithErrors of
|
||||
case ContentCache.pagesWithErrors contentCache of
|
||||
[] ->
|
||||
let
|
||||
requests =
|
||||
siteMetadata
|
||||
|> Result.andThen
|
||||
(\metadata ->
|
||||
staticResponseForPage metadata config.view
|
||||
)
|
||||
Result.andThen
|
||||
(\metadata ->
|
||||
staticResponseForPage metadata config.view
|
||||
)
|
||||
siteMetadata
|
||||
|
||||
staticResponses : StaticResponses
|
||||
staticResponses =
|
||||
@ -359,11 +359,11 @@ init toModel contentCache siteMetadata config flags =
|
||||
pageErrors ->
|
||||
let
|
||||
requests =
|
||||
siteMetadata
|
||||
|> Result.andThen
|
||||
(\metadata ->
|
||||
staticResponseForPage metadata config.view
|
||||
)
|
||||
Result.andThen
|
||||
(\metadata ->
|
||||
staticResponseForPage metadata config.view
|
||||
)
|
||||
siteMetadata
|
||||
|
||||
staticResponses : StaticResponses
|
||||
staticResponses =
|
||||
@ -453,25 +453,25 @@ update siteMetadata config msg model =
|
||||
case msg of
|
||||
GotStaticHttpResponse { request, response } ->
|
||||
let
|
||||
--_ =
|
||||
-- Debug.log "Got response" request.masked.url
|
||||
-- _ =
|
||||
-- Debug.log "Got response" request.masked.url
|
||||
--
|
||||
updatedModel =
|
||||
(case response of
|
||||
Ok okResponse ->
|
||||
staticResponsesUpdate
|
||||
{ request = request
|
||||
, response =
|
||||
response |> Result.mapError (\_ -> ())
|
||||
, response = Result.mapError (\_ -> ()) response
|
||||
}
|
||||
model
|
||||
|
||||
Err error ->
|
||||
{ model
|
||||
| errors =
|
||||
model.errors
|
||||
++ [ { title = "Static HTTP Error"
|
||||
, message =
|
||||
List.append
|
||||
model.errors
|
||||
[ { title = "Static HTTP Error"
|
||||
, message =
|
||||
[ Terminal.text "I got an error making an HTTP request to this URL: "
|
||||
|
||||
-- TODO include HTTP method, headers, and body
|
||||
@ -496,16 +496,15 @@ update siteMetadata config msg model =
|
||||
Pages.Http.NetworkError ->
|
||||
Terminal.text "Network error"
|
||||
]
|
||||
, fatal = True
|
||||
}
|
||||
]
|
||||
, fatal = True
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|> staticResponsesUpdate
|
||||
-- TODO for hash pass in RequestDetails here
|
||||
{ request = request
|
||||
, response =
|
||||
response |> Result.mapError (\_ -> ())
|
||||
, response = Result.mapError (\_ -> ()) response
|
||||
}
|
||||
|
||||
( updatedAllRawResponses, effect ) =
|
||||
@ -527,10 +526,9 @@ performStaticHttpRequests allRawResponses secrets staticRequests =
|
||||
staticRequests
|
||||
|> List.map
|
||||
(\( pagePath, request ) ->
|
||||
StaticHttpRequest.resolveUrls request
|
||||
(allRawResponses
|
||||
|> dictCompact
|
||||
)
|
||||
allRawResponses
|
||||
|> dictCompact
|
||||
|> StaticHttpRequest.resolveUrls request
|
||||
|> Tuple.second
|
||||
)
|
||||
|> List.concat
|
||||
@ -539,10 +537,13 @@ performStaticHttpRequests allRawResponses secrets staticRequests =
|
||||
-- |> Set.toList
|
||||
|> List.map
|
||||
(\urlBuilder ->
|
||||
Secrets.lookup secrets urlBuilder
|
||||
urlBuilder
|
||||
|> Secrets.lookup secrets
|
||||
|> Result.map
|
||||
(\unmasked ->
|
||||
{ unmasked = unmasked, masked = Secrets.maskedLookup urlBuilder }
|
||||
{ unmasked = unmasked
|
||||
, masked = Secrets.maskedLookup urlBuilder
|
||||
}
|
||||
)
|
||||
)
|
||||
|> combineMultipleErrors
|
||||
@ -636,41 +637,47 @@ staticResponsesUpdate : { request : { masked : RequestDetails, unmasked : Reques
|
||||
staticResponsesUpdate newEntry model =
|
||||
let
|
||||
updatedAllResponses =
|
||||
model.allRawResponses
|
||||
-- @@@@@@@@@ TODO handle errors here, change Dict to have `Result` instead of `Maybe`
|
||||
|> Dict.insert (HashRequest.hash newEntry.request.masked) (Just (newEntry.response |> Result.withDefault "TODO"))
|
||||
-- @@@@@@@@@ TODO handle errors here, change Dict to have `Result` instead of `Maybe`
|
||||
Dict.insert
|
||||
(HashRequest.hash newEntry.request.masked)
|
||||
(Just <| Result.withDefault "TODO" newEntry.response)
|
||||
model.allRawResponses
|
||||
in
|
||||
{ model
|
||||
| allRawResponses = updatedAllResponses
|
||||
, staticResponses =
|
||||
model.staticResponses
|
||||
|> Dict.map
|
||||
(\pageUrl entry ->
|
||||
case entry of
|
||||
NotFetched request rawResponses ->
|
||||
Dict.map
|
||||
(\pageUrl entry ->
|
||||
case entry of
|
||||
NotFetched request rawResponses ->
|
||||
let
|
||||
realUrls =
|
||||
updatedAllResponses
|
||||
|> dictCompact
|
||||
|> StaticHttpRequest.resolveUrls request
|
||||
|> Tuple.second
|
||||
|> List.map Secrets.maskedLookup
|
||||
|> List.map HashRequest.hash
|
||||
|
||||
includesUrl =
|
||||
List.member
|
||||
(HashRequest.hash newEntry.request.masked)
|
||||
realUrls
|
||||
in
|
||||
if includesUrl then
|
||||
let
|
||||
realUrls =
|
||||
StaticHttpRequest.resolveUrls request
|
||||
(updatedAllResponses |> dictCompact)
|
||||
|> Tuple.second
|
||||
|> List.map Secrets.maskedLookup
|
||||
|> List.map HashRequest.hash
|
||||
|
||||
includesUrl =
|
||||
List.member (HashRequest.hash newEntry.request.masked)
|
||||
realUrls
|
||||
in
|
||||
if includesUrl then
|
||||
let
|
||||
updatedRawResponses =
|
||||
updatedRawResponses =
|
||||
Dict.insert
|
||||
(HashRequest.hash newEntry.request.masked)
|
||||
newEntry.response
|
||||
rawResponses
|
||||
|> Dict.insert (HashRequest.hash newEntry.request.masked) newEntry.response
|
||||
in
|
||||
NotFetched request updatedRawResponses
|
||||
in
|
||||
NotFetched request updatedRawResponses
|
||||
|
||||
else
|
||||
entry
|
||||
)
|
||||
else
|
||||
entry
|
||||
)
|
||||
model.staticResponses
|
||||
}
|
||||
|
||||
|
||||
@ -704,20 +711,21 @@ sendStaticResponsesIfDone config siteMetadata mode secrets allRawResponses error
|
||||
let
|
||||
usableRawResponses : Dict String String
|
||||
usableRawResponses =
|
||||
rawResponses
|
||||
|> Dict.Extra.filterMap
|
||||
(\key value ->
|
||||
value
|
||||
|> Result.map Just
|
||||
|> Result.withDefault Nothing
|
||||
)
|
||||
Dict.Extra.filterMap
|
||||
(\key value ->
|
||||
value
|
||||
|> Result.map Just
|
||||
|> Result.withDefault Nothing
|
||||
)
|
||||
rawResponses
|
||||
|
||||
hasPermanentError =
|
||||
StaticHttpRequest.permanentError request usableRawResponses
|
||||
usableRawResponses
|
||||
|> StaticHttpRequest.permanentError request
|
||||
|> isJust
|
||||
|
||||
hasPermanentHttpError =
|
||||
not <| List.isEmpty errors
|
||||
not (List.isEmpty errors)
|
||||
|
||||
--|> List.any
|
||||
-- (\error ->
|
||||
@ -860,7 +868,7 @@ sendStaticResponsesIfDone config siteMetadata mode secrets allRawResponses error
|
||||
PagePath.toString pagePath
|
||||
|
||||
currentContentPath =
|
||||
"/" ++ (path |> String.join "/")
|
||||
String.join "/" path
|
||||
in
|
||||
if pagePathToGenerate == currentContentPath then
|
||||
Just body
|
||||
@ -962,13 +970,12 @@ encodeStaticResponses mode staticResponses =
|
||||
NotFetched request rawResponsesDict ->
|
||||
let
|
||||
relevantResponses =
|
||||
rawResponsesDict
|
||||
|> Dict.map
|
||||
(\key value ->
|
||||
value
|
||||
-- TODO avoid running this code at all if there are errors here
|
||||
|> Result.withDefault ""
|
||||
)
|
||||
Dict.map
|
||||
(\_ ->
|
||||
-- TODO avoid running this code at all if there are errors here
|
||||
Result.withDefault ""
|
||||
)
|
||||
rawResponsesDict
|
||||
|
||||
strippedResponses : Dict String String
|
||||
strippedResponses =
|
||||
|
43
src/Pages/Internal/String.elm
Normal file
43
src/Pages/Internal/String.elm
Normal file
@ -0,0 +1,43 @@
|
||||
module Pages.Internal.String exposing (..)
|
||||
|
||||
{-| Remove a piece from the beginning of a string until it's not there anymore.
|
||||
|
||||
>>> chopStart "{" "{{{<-"
|
||||
"<-"
|
||||
|
||||
-}
|
||||
|
||||
|
||||
chopStart : String -> String -> String
|
||||
chopStart needle string =
|
||||
if String.startsWith needle string then
|
||||
string
|
||||
|> String.dropLeft (String.length needle)
|
||||
|> chopStart needle
|
||||
|
||||
else
|
||||
string
|
||||
|
||||
|
||||
{-| Remove a piece from the end of a string until it's not there anymore.
|
||||
|
||||
>>> chopEnd "}" "->}}}"
|
||||
"->"
|
||||
|
||||
-}
|
||||
chopEnd : String -> String -> String
|
||||
chopEnd needle string =
|
||||
if String.endsWith needle string then
|
||||
string
|
||||
|> String.dropRight (String.length needle)
|
||||
|> chopEnd needle
|
||||
|
||||
else
|
||||
string
|
||||
|
||||
|
||||
{-| Removes `/` characters from both ends of a string.
|
||||
-}
|
||||
chopForwardSlashes : String -> String
|
||||
chopForwardSlashes =
|
||||
chopStart "/" >> chopEnd "/"
|
@ -259,7 +259,7 @@ toJson config =
|
||||
)
|
||||
, ( "serviceworker"
|
||||
, Encode.object
|
||||
[ ( "src", Encode.string "/service-worker.js" )
|
||||
[ ( "src", Encode.string "../service-worker.js" )
|
||||
, ( "scope", Encode.string "/" )
|
||||
, ( "type", Encode.string "" )
|
||||
, ( "update_via_cache", Encode.string "none" )
|
||||
|
@ -44,7 +44,7 @@ This gives you a record, based on your local `content` directory, that lets you
|
||||
Pages.pages.index
|
||||
|
||||
-- PagePath.toString homePath
|
||||
-- => "/"
|
||||
-- => ""
|
||||
|
||||
or
|
||||
|
||||
@ -56,7 +56,7 @@ or
|
||||
Pages.pages.blog.helloWorld
|
||||
|
||||
-- PagePath.toString helloWorldPostPath
|
||||
-- => "/blog/hello-world"
|
||||
-- => "blog/hello-world"
|
||||
|
||||
Note that in the `hello-world` example it changes from the kebab casing of the actual
|
||||
URL to camelCasing for the record key.
|
||||
@ -93,7 +93,7 @@ type PagePath key
|
||||
| External String
|
||||
|
||||
|
||||
{-| Gives you the page's absolute URL as a String. This is useful for constructing links:
|
||||
{-| Gives you the page's relative URL as a String. This is useful for constructing links:
|
||||
|
||||
import Html exposing (Html, a)
|
||||
import Html.Attributes exposing (href)
|
||||
@ -115,8 +115,7 @@ toString : PagePath key -> String
|
||||
toString path =
|
||||
case path of
|
||||
Internal rawPath ->
|
||||
"/"
|
||||
++ (rawPath |> String.join "/")
|
||||
String.join "/" rawPath
|
||||
|
||||
External url ->
|
||||
url
|
||||
|
@ -20,10 +20,10 @@ And your StaticHttp request in your Elm code looks like this:
|
||||
|
||||
StaticHttp.request
|
||||
(Secrets.succeed
|
||||
(\githubToken stripeoken ->
|
||||
(\apiKey githubToken ->
|
||||
{ url = "https://api.github.com/repos/dillonkearns/elm-pages?apiKey=" ++ apiKey
|
||||
, method = "GET"
|
||||
, headers = [ ( "Authorization", "Bearer " ++ bearer ) ]
|
||||
, headers = [ ( "Authorization", "Bearer " ++ githubToken ) ]
|
||||
}
|
||||
)
|
||||
|> Secrets.with "API_KEY"
|
||||
|
@ -15,7 +15,7 @@ The key differences are:
|
||||
|
||||
- `StaticHttp.Request`s are performed once at build time (`Http.Request`s are performed at runtime, at whenever point you perform them)
|
||||
- `StaticHttp.Request`s strip out unused JSON data from the data your decoder doesn't touch to minimize the JSON payload
|
||||
- `StaticHttp.Request`s can use [`Pages.Secrets`](Pages.Secrets) to securely use credentials from your environemnt variables which are completely masked in the production assets.
|
||||
- `StaticHttp.Request`s can use [`Pages.Secrets`](Pages.Secrets) to securely use credentials from your environment variables which are completely masked in the production assets.
|
||||
- `StaticHttp.Request`s have a built-in `StaticHttp.andThen` that allows you to perform follow-up requests without using tasks
|
||||
|
||||
|
||||
@ -167,10 +167,8 @@ map fn requestInfo =
|
||||
{-| Helper to remove an inner layer of Request wrapping.
|
||||
-}
|
||||
resolve : Request (List (Request value)) -> Request (List value)
|
||||
resolve topRequest =
|
||||
topRequest
|
||||
|> andThen
|
||||
(\continuationRequests -> combine continuationRequests)
|
||||
resolve =
|
||||
andThen combine
|
||||
|
||||
|
||||
{-| Turn a list of `StaticHttp.Request`s into a single one.
|
||||
@ -208,9 +206,8 @@ resolve topRequest =
|
||||
|
||||
-}
|
||||
combine : List (Request value) -> Request (List value)
|
||||
combine requests =
|
||||
requests
|
||||
|> List.foldl (map2 (::)) (succeed [])
|
||||
combine =
|
||||
List.foldl (map2 (::)) (succeed [])
|
||||
|
||||
|
||||
{-| Like map, but it takes in two `Request`s.
|
||||
@ -462,15 +459,15 @@ get :
|
||||
-> Request a
|
||||
get url decoder =
|
||||
request
|
||||
(url
|
||||
|> Secrets.map
|
||||
(\okUrl ->
|
||||
{ url = okUrl
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
, body = emptyBody
|
||||
}
|
||||
)
|
||||
(Secrets.map
|
||||
(\okUrl ->
|
||||
{ url = okUrl
|
||||
, method = "GET"
|
||||
, headers = []
|
||||
, body = emptyBody
|
||||
}
|
||||
)
|
||||
url
|
||||
)
|
||||
decoder
|
||||
|
||||
|
@ -42,7 +42,7 @@ all =
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
"""{ "stargazer_count": 86 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{"stargazer_count":86}"""
|
||||
)
|
||||
@ -69,7 +69,7 @@ all =
|
||||
"NEXT-REQUEST"
|
||||
"""null"""
|
||||
|> expectSuccess
|
||||
[ ( "/elm-pages"
|
||||
[ ( "elm-pages"
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """null"""
|
||||
)
|
||||
@ -162,7 +162,7 @@ all =
|
||||
"url10"
|
||||
"""{"image": "image10.jpg"}"""
|
||||
|> expectSuccess
|
||||
[ ( "/elm-pages"
|
||||
[ ( "elm-pages"
|
||||
, [ ( get "https://pokeapi.co/api/v2/pokemon/"
|
||||
, """[{"url":"url1"},{"url":"url2"},{"url":"url3"},{"url":"url4"},{"url":"url5"},{"url":"url6"},{"url":"url7"},{"url":"url8"},{"url":"url9"},{"url":"url10"}]"""
|
||||
)
|
||||
@ -218,13 +218,13 @@ all =
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages-starter"
|
||||
"""{ "stargazer_count": 22 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/elm-pages"
|
||||
[ ( "elm-pages"
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{"stargazer_count":86}"""
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( "/elm-pages-starter"
|
||||
, ( "elm-pages-starter"
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages-starter"
|
||||
, """{"stargazer_count":22}"""
|
||||
)
|
||||
@ -243,7 +243,7 @@ all =
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{"stargazer_count":86}"""
|
||||
)
|
||||
@ -272,7 +272,7 @@ all =
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{ "stargazer_count": 86, "unused_field": 123 }"""
|
||||
)
|
||||
@ -299,7 +299,7 @@ all =
|
||||
"https://example.com/file.txt"
|
||||
"This is a raw text file."
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://example.com/file.txt"
|
||||
, "This is a raw text file."
|
||||
)
|
||||
@ -339,7 +339,7 @@ all =
|
||||
(expectErrorsPort
|
||||
"""-- STATIC HTTP DECODING ERROR ----------------------------------------------------- elm-pages
|
||||
|
||||
/
|
||||
|
||||
|
||||
String was not uppercased"""
|
||||
)
|
||||
@ -363,7 +363,7 @@ String was not uppercased"""
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( { method = "POST"
|
||||
, url = "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, headers = []
|
||||
@ -394,7 +394,7 @@ String was not uppercased"""
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages-starter"
|
||||
"""{ "stargazer_count": 50, "unused_field": 456 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{"stargazer_count":100}"""
|
||||
)
|
||||
@ -422,7 +422,7 @@ String was not uppercased"""
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages-starter"
|
||||
"""{ "stargazer_count": 50, "unused_field": 456 }"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, """{"stargazer_count":100}"""
|
||||
)
|
||||
@ -439,7 +439,7 @@ String was not uppercased"""
|
||||
, StaticHttp.succeed ()
|
||||
)
|
||||
]
|
||||
|> expectSuccess [ ( "/", [] ) ]
|
||||
|> expectSuccess [ ( "", [] ) ]
|
||||
, test "the port sends out when there are duplicate http requests for the same page" <|
|
||||
\() ->
|
||||
start
|
||||
@ -454,7 +454,7 @@ String was not uppercased"""
|
||||
"http://example.com"
|
||||
"""null"""
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( get "http://example.com"
|
||||
, """null"""
|
||||
)
|
||||
@ -478,7 +478,7 @@ String was not uppercased"""
|
||||
(expectErrorsPort
|
||||
"""-- STATIC HTTP DECODING ERROR ----------------------------------------------------- elm-pages
|
||||
|
||||
/elm-pages
|
||||
elm-pages
|
||||
|
||||
I encountered some errors while decoding this JSON:
|
||||
|
||||
@ -591,7 +591,7 @@ Body: """)
|
||||
}
|
||||
)
|
||||
|> expectSuccess
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( { method = "GET"
|
||||
, url = "https://api.github.com/repos/dillonkearns/elm-pages?apiKey=<API_KEY>"
|
||||
, headers =
|
||||
@ -650,7 +650,6 @@ start pages =
|
||||
|> Dict.get
|
||||
(page.path
|
||||
|> PagePath.toString
|
||||
|> String.dropLeft 1
|
||||
|> String.split "/"
|
||||
|> List.filter (\pathPart -> pathPart /= "")
|
||||
)
|
||||
@ -806,7 +805,7 @@ starDecoder =
|
||||
|
||||
|
||||
thingy =
|
||||
[ ( "/"
|
||||
[ ( ""
|
||||
, [ ( { method = "GET"
|
||||
, url = "https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, headers = []
|
||||
|
Loading…
Reference in New Issue
Block a user