Skip to content

Commit

Permalink
HTML redirects
Browse files Browse the repository at this point in the history
Fix #42
  • Loading branch information
cirosantilli committed May 4, 2020
1 parent cb4a143 commit 9bb0a12
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 116 deletions.
58 changes: 42 additions & 16 deletions README.ciro
Expand Up @@ -1800,6 +1800,14 @@ For example, when the \x[internal-cross-references][ID of a node is derived from
In this example, see how the contents of the code block \c[`with code`] were rendered as plaintext and used for the ID.
``

=== Security

Cirodown is designed to be XSS safe by default. If we add any non-XSS safe constructs, these should be enabled with a non-default flag.

Of course, we are walking on eggs, and this is hard to assert, so the best thing to do later on will be to parse the output e.g. with https://developer.mozilla.org/en-US/docs/Web/API/DOMParser[`DOMParser`] to ensure that it is valid and does not contain any `script` tags, but it is not as simple as that: https://stackoverflow.com/questions/37435077/execute-javascript-for-xss-without-script-tags/61588322#61588322

About server safety, we have to think about it. E.g. stuff like \x[prepublish] already allows ACE.

== Tooling

Unlike all languages which rely on ad-hoc tooling, we will support every single tool that is required and feasible to be in this repository in this repository, in a centralized manner.
Expand Down Expand Up @@ -2104,6 +2112,26 @@ Cirodown configuration file that affects the behaviour of cirodown for all files

`cirodown.json` not used for input from stdin, since we are mostly doing quick tests in that case.

===== `media_base_url`
{id=media-base-url}

TODO implement set the base URL where media will be served from.

Default: for a GitHub repository called `my-tutorial`, append the `-media` suffix and use `my-tutorial-media`.

Further rationale at: \x[where-to-store-images].

===== `media_source_type`
{id=media-source-type}

TODO implement:
* `local`: tracked in the current Git repository, no further magic is done
\l[
`media_repo`: tracked in a separate media repository, defaults to `../<project-name>-media/`

When \x[publish][publishing], Cirodown searches for files that were used in the build but not added to the media directory, and automatically does a `git add`, `commit` and `push` for you!
]

===== `prepublish`
{id=prepublish}

Expand All @@ -2120,25 +2148,23 @@ This means that you can use this script to place or remove files from the final

If the `prepublish` script returns with a non-zero exit value, the publish is aborted.

===== `media_source_type`
{id=media-source-type}
===== `redirects`
{id=redirects}

TODO implement:
* `local`: tracked in the current Git repository, no further magic is done
\l[
`media_repo`: tracked in a separate media repository, defaults to `../<project-name>-media/`

When \x[publish][publishing], Cirodown searches for files that were used in the build but not added to the media directory, and automatically does a `git add`, `commit` and `push` for you!
]

===== `media_base_url`
{id=media-base-url}

TODO implement set the base URL where media will be served from.
Create HTML redirects from \x[the-toplevel-header][toplevel headers] to arbitrary IDs, for example:
``
"redirects": {
"a-toplevel-directory": "not-readme",
"quick-start-redirect-test": "quick-start"
},
``
The HTML files are generated whenever a directory compilation is required, e.g. as in `cirodown .`.

Default: for a GitHub repository called `my-tutorial`, append the `-media` suffix and use `my-tutorial-media`.
The sources must be toplevel headers for now, because it is harder to implement redirections from `#someid` as that would require JavaScript: https://github.com/cirosantilli/cirodown/issues/48[].

Further rationale at: \x[where-to-store-images].
This feature can be seen live on this document by visiting our tests:
* \a[not-readme-redirect-test]: redirects to \x[not-readme]
* \a[quick-start-redirect-test]: redirects to \x[quick-start]

=== Editor with preview

Expand Down
73 changes: 55 additions & 18 deletions cirodown
Expand Up @@ -20,7 +20,7 @@ const cirodown_nodejs = require('cirodown/nodejs');
const CIRODOWN_EXT = '.ciro';
const CIRODOWN_JSON = 'cirodown.json';
const SASS_EXT = '.scss';
const OUT_DIRNAME = 'out';
const TMP_DIRNAME = 'out';
const DEFAULT_IGNORE_BASENAMES = [
'.git',
'.gitignore',
Expand All @@ -29,7 +29,7 @@ const DEFAULT_IGNORE_BASENAMES = [
'node_modules',
'package-lock.json',
'package.json',
OUT_DIRNAME,
TMP_DIRNAME,
cirodown_nodejs.PACKAGE_SASS_BASENAME,
];
const DEFAULT_IGNORE_BASENAMES_SET = new Set(DEFAULT_IGNORE_BASENAMES);
Expand Down Expand Up @@ -471,13 +471,13 @@ if (commander.helpMacros) {
// Not in a git repo.
}
}
let outdir;
if (commander.outdir === undefined) {
outdir = '.';
} else {
outdir = commander.outdir;
}
if (commander.generate || commander.generateMultifile) {
let outdir;
if (commander.outdir !== undefined) {
outdir = commander.outdir;
} else {
outdir = '.';
}
fs.mkdirSync(outdir, {recursive: true});

// Generate package.json.
Expand Down Expand Up @@ -553,7 +553,7 @@ A link to another file: \\x[and-now-an-include]
cli_error(`input_path does not exist: "${inputPath}"`);
}
let input_path_is_file;
let outdir;
let tmpdir;
let publish_dir;
let publish_out_dir;
let cmd_options = {
Expand All @@ -562,28 +562,28 @@ A link to another file: \\x[and-now-an-include]
}
if (fs.lstatSync(inputPath).isFile()) {
input_path_is_file = true;
outdir = OUT_DIRNAME;
tmpdir = TMP_DIRNAME;
} else {
input_path_is_file = false;
outdir = path.join(inputPath, OUT_DIRNAME);
tmpdir = path.join(inputPath, TMP_DIRNAME);
if (publish) {
publish_dir = path.join(outdir, 'publish');
publish_dir = path.join(tmpdir, 'publish');
publish_git_dir = path.join(publish_dir, '.git');
if (fs.existsSync(publish_git_dir)) {
// This cleanup has to be done before the database initialization.
cmd_get_stdout('git', ['-C', publish_dir, 'clean', '-x', '-d', '-f'], cmd_options);
}
outdir = path.join(publish_dir, OUT_DIRNAME);
publish_out_dir = path.join(publish_dir, OUT_DIRNAME);
tmpdir = path.join(publish_dir, TMP_DIRNAME);
publish_out_dir = path.join(publish_dir, TMP_DIRNAME);
}
if (commander.outfile !== undefined) {
cli_error(`--outfile given but multiple output files must be generated, maybe you want --outdir?`);
}
}

// Setup the ID database.
fs.mkdirSync(outdir, {recursive: true});
const db_path = path.join(outdir, 'db.sqlite3');
fs.mkdirSync(tmpdir, {recursive: true});
const db_path = path.join(tmpdir, 'db.sqlite3');
const ids_table_name = 'ids';
const includes_table_name = 'includes';
const db = new better_sqlite3(db_path);
Expand Down Expand Up @@ -618,7 +618,6 @@ A link to another file: \\x[and-now-an-include]
convert_input_options.db = db;
convert_input_options.ids_table_name = ids_table_name;
convert_input_options.includes_table_name = includes_table_name;

if (commander.watch) {
if (publish) {
cli_error('--publish and --watch are incompatible');
Expand Down Expand Up @@ -690,7 +689,7 @@ A link to another file: \\x[and-now-an-include]
cmd_get_stdout('git', ['-C', convert_input_options.outdir, 'rm', '-r', '-f', '.'], cmd_options);
} else {
actual_input = inputPath;
convert_input_options.outdir = commander.outdir;
convert_input_options.outdir = outdir;
}

// Do the actual conversion.
Expand Down Expand Up @@ -740,6 +739,44 @@ A link to another file: \\x[and-now-an-include]
}
}
}
// Generate redirects.
// https://cirosantilli.com/cirodown#redirects
if (!input_path_is_file) {
const id_provider = new SqliteIdProvider(convert_input_options);
for (let redirect_src_id in cirodown_json.redirects) {
const redirect_target_id = cirodown_json.redirects[redirect_src_id];
const redirect_href = cirodown.x_href(
id_provider.get(redirect_target_id), {
// This will need changing when we do:
// https://github.com/cirosantilli/cirodown/issues/44
// The simple solution is to just make all redirects point
// to the index in that case.
include_path_set: new Set(),
options: {
input_path: redirect_src_id,
html_x_extension: html_x_extension,
}
}
);
// https://stackoverflow.com/questions/10178304/what-is-the-best-approach-for-redirection-of-old-pages-in-jekyll-and-github-page/36848440#36848440
fs.writeFileSync(path.join(outdir, redirect_src_id + '.html'),
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Redirecting...</title>
<link rel="canonical" href="${redirect_href}"/>
<meta http-equiv="refresh" content="0;url=${redirect_href}" />
</head>
<body>
<h1>Redirecting...</h1>
<a href="${redirect_href}">Click here if you are not redirected.<a>
<script>location='${redirect_href}'</script>
</body>
</html>
`);
}
}
}
if (output !== undefined) {
if (commander.outfile === undefined) {
Expand Down
8 changes: 6 additions & 2 deletions cirodown.json
@@ -1,4 +1,8 @@
{
"prepublish": "prepublish",
"template": "main.liquid.html"
"prepublish": "prepublish",
"redirects": {
"not-readme-redirect-test": "not-readme",
"quick-start-redirect-test": "quick-start"
},
"template": "main.liquid.html"
}

0 comments on commit 9bb0a12

Please sign in to comment.