From: Huck Boles Date: Sat, 3 Jun 2023 01:09:15 +0000 (-0500) Subject: release: v0.1.3 X-Git-Url: https://git.huck.website/?a=commitdiff_plain;h=3edce7b9c6b02579dff95b0f98888975e2e62b11;p=metaforge.git release: v0.1.3 --- diff --git a/.gitignore b/.gitignore index d8450c1..16e17a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target files/test_site/build -files/README/build +docs/build files/bench_site/build bacon.toml diff --git a/Cargo.lock b/Cargo.lock index f9c80f5..1f37171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "anes" version = "0.1.6" @@ -154,9 +163,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" dependencies = [ "clap_builder", "clap_derive", @@ -165,9 +174,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" dependencies = [ "anstream", "anstyle", @@ -178,14 +187,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b" dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -209,6 +218,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cpufeatures" version = "0.2.7" @@ -307,6 +322,30 @@ dependencies = [ "typenum", ] +[[package]] +name = "css-minify" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd" +dependencies = [ + "derive_more", + "indexmap", + "nom", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -498,12 +537,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -516,11 +558,12 @@ dependencies = [ [[package]] name = "metaforge" -version = "0.1.1" +version = "0.1.3" dependencies = [ - "clap 4.3.0", + "clap 4.3.1", "criterion", "eyre", + "minify-html", "pandoc", "pest", "pest_derive", @@ -528,6 +571,46 @@ dependencies = [ "thiserror", ] +[[package]] +name = "minify-html" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4d9147754a49e80557df835eb59e743eab1bf75410a134f55dc4b9dbb692ad" +dependencies = [ + "aho-corasick", + "css-minify", + "lazy_static", + "memchr", + "minify-js", + "rustc-hash", +] + +[[package]] +name = "minify-js" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4" +dependencies = [ + "lazy_static", + "parse-js", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -549,9 +632,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" [[package]] name = "oorandom" @@ -574,6 +657,17 @@ dependencies = [ "itertools 0.8.2", ] +[[package]] +name = "parse-js" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5" +dependencies = [ + "aho-corasick", + "lazy_static", + "memchr", +] + [[package]] name = "pest" version = "2.6.0" @@ -604,7 +698,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -701,6 +795,21 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.19" @@ -736,6 +845,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.163" @@ -753,7 +868,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -784,6 +899,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.18" @@ -818,7 +944,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -892,7 +1018,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-shared", ] @@ -914,7 +1040,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index a515fcc..30560cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "metaforge" -version = "0.1.2" +version = "0.1.3" edition = "2021" [dependencies] @@ -11,6 +11,7 @@ eyre = "0.6" pest = "2" pest_derive = "2" rayon = "1.7" +minify-html = "0.11" [dev-dependencies] criterion = "0.4" diff --git a/README.md b/README.md index 4614ad0..6e2e3b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# metaforge - v0.1.2 +# metaforge - v0.1.3 a pattern driven static site generator for extensible snippet insertion. @@ -15,9 +15,10 @@ a pattern driven static site generator for extensible snippet insertion. ## about metaforge is a static site generator that lets you write something once, and re-use it -across your site. it requires previous knowledge of html, and doesn't come with pre-made -themes or templates except for a completely bare skeleton directory. it gives you extremely -fine grained control over the generated html, customizing each pattern to a source file +across your site. + +metaforge doesn't come with pre-made themes or templates except for a completely bare skeleton directory. +it gives you extremely fine grained control over the generated html, customizing each pattern to a source file with variables and mappable arrays. metaforge also lets you write metapatterns that contain classes, masking choices to a @@ -31,7 +32,7 @@ this can be set on both a site and file level, allowing single files in the site such as an rss feed. metaforge can also technically translate between any two document formats pandoc supports, but nothing other than the default markdown to html gets tested. -the full documentation is available in this repository under the **files/README/source/docs** +the full documentation is available in this repository under the **docs/source** directory. it's currently setup as a working example of a metaforge site, so you can poke around and see one way a site can be setup. @@ -41,4 +42,4 @@ and see one way a site can be setup. this command can be run as many times as needed to regenerate the documentation, and is reccomended after upgrading to a new version to see what's changed. the generated docs will -be available in **files/README/build**, and can be looked at in any web browser. +be available in **docs/build**, and can be looked at in any web browser. diff --git a/benches/parallel.rs b/benches/parallel.rs index b31ed61..9e7a694 100644 --- a/benches/parallel.rs +++ b/benches/parallel.rs @@ -13,7 +13,7 @@ pub fn parallel_build_dir(c: &mut Criterion) { opts.clean = true; opts.parallel = true; - c.bench_function("build dir", |b| { + c.bench_function("parallel build", |b| { if opts.build.exists() { std::fs::remove_dir_all(&opts.build).expect("clean build dir"); } diff --git a/files/README/pattern/base/default.meta b/docs/pattern/base/default.meta similarity index 100% rename from files/README/pattern/base/default.meta rename to docs/pattern/base/default.meta diff --git a/files/README/pattern/body/default.meta b/docs/pattern/body/default.meta similarity index 100% rename from files/README/pattern/body/default.meta rename to docs/pattern/body/default.meta diff --git a/files/README/pattern/foot/default.meta b/docs/pattern/foot/default.meta similarity index 100% rename from files/README/pattern/foot/default.meta rename to docs/pattern/foot/default.meta diff --git a/files/README/pattern/head/default.meta b/docs/pattern/head/default.meta similarity index 100% rename from files/README/pattern/head/default.meta rename to docs/pattern/head/default.meta diff --git a/files/README/source/default.meta b/docs/source/default.meta similarity index 72% rename from files/README/source/default.meta rename to docs/source/default.meta index 3c3a9fb..6c8c868 100644 --- a/files/README/source/default.meta +++ b/docs/source/default.meta @@ -1,5 +1,5 @@ ${ author = 'huck boles' - version = '0.1.2' + version = '0.1.3' home = './index.html' } diff --git a/files/README/source/docs/default.meta b/docs/source/docs/default.meta similarity index 65% rename from files/README/source/docs/default.meta rename to docs/source/docs/default.meta index 8103058..cf0bd60 100644 --- a/files/README/source/docs/default.meta +++ b/docs/source/docs/default.meta @@ -2,9 +2,9 @@ ${ -{ index is a folder up in this directory } home = '../index.html' -{ using substitutions for example code so it doesn't mess up parsing } - var = '${ ' - arr = '@{ ' - pat = '&{ ' - head = '#{ ' - com = '-{ ' + var = '${' + arr = '@{' + pat = '&{' + head = '#{' + com = '-{' } diff --git a/files/README/source/docs/definitions.meta b/docs/source/docs/definitions.meta similarity index 97% rename from files/README/source/docs/definitions.meta rename to docs/source/docs/definitions.meta index 2d790ec..7543ffa 100644 --- a/files/README/source/docs/definitions.meta +++ b/docs/source/docs/definitions.meta @@ -1,7 +1,6 @@ ${ title = 'definitions' description = 'definining variables and patterns' - arr_sub = '@{' } ## rules @@ -73,7 +72,7 @@ value normally. foo.bar = [ 'foobar', 'foobaz' ] ${com} this will copy pattern/foo/*.meta twice, inserting 'foobar and 'foobaz' - once each at the location of ${arr_sub}bar} } + once each at the location of ${arr}bar} } } ${com} all of these patterns are only defined for this file } diff --git a/files/README/source/docs/expansions.meta b/docs/source/docs/expansions.meta similarity index 77% rename from files/README/source/docs/expansions.meta rename to docs/source/docs/expansions.meta index f3d9212..2fb7ae0 100644 --- a/files/README/source/docs/expansions.meta +++ b/docs/source/docs/expansions.meta @@ -1,9 +1,6 @@ ${ title = 'expansions' description = 'expanding variables and patterns in a file' - var_sub = '${' - arr_sub = '@{' - pat_sub = '&{' } ## syntax @@ -15,11 +12,11 @@ when the file is built ### examples - ...this is a string with a ${var_sub}variable} to be expanded... + ...this is a string with a ${var}variable} to be expanded... - ...this line has a ${pat_sub}pattern} inside of it... + ...this line has a ${pat}pattern} inside of it... - ...this ${arr_sub}array} will be replaced... + ...this ${arr}array} will be replaced... ## behavior @@ -41,11 +38,11 @@ don't need any extra expansion. quux = BLANK } - pattern [foo]:

${var_sub}baz} ${var}quux}

+ pattern [foo]:

${var}baz} ${var}quux}

expanded [foo]:

foo

- pattern [bar]:

${var_sub}baz} ${var}quux}

+ pattern [bar]:

${var}baz} ${var}quux}

expanded [bar]:

quux

@@ -58,7 +55,7 @@ parts of patterns. #### example - pattern [foo]:

${arr_sub}bar}

+ pattern [foo]:

${arr}bar}

defintion: ${arr} foo.bar = ['foo', 'bar', 'baz'] } @@ -89,20 +86,28 @@ processed source from the original calling file in the source directory. this goes through an extra step right before insertion, calling pandoc on the file to convert between the chosen filetypes. +***SOURCE*** can also be used as a standin for the source directory while writing expansions, +allowing patterns to call the same source file every time, or source files to expand other +source files. + +### example + ...lorem ${pat}SOURCE.foo.bar} ipsum dolor... + once the filename is determined, it is parsed and expands any contained variables, arrays and patterns. if it is a ***SOURCE*** pattern, it is converted to html after the expansions. the expanded pattern is then inserted in place of the calling identifier. ## building -as each file is built, the first thing expanded is the relevant **base/[FILE].meta** pattern, -so it is required to have at least a **default.meta** in the **pattern/base** directory +as each source file is built, the first thing expanded is the defined or default +**[PATTERN]/base/[FILE].meta** pattern, so it is required to have at least a **default.meta** +in the **pattern/base** directory ### example - pattern [base]: ${pat_sub}body} + pattern [base]: ${pat}body} - pattern [body]: ${pat_sub}SOURCE} + pattern [body]: ${pat}SOURCE} source [SOURCE]: foo *bar* baz diff --git a/files/README/source/docs/flags.meta b/docs/source/docs/flags.meta similarity index 93% rename from files/README/source/docs/flags.meta rename to docs/source/docs/flags.meta index 4a00fbf..e5fa294 100644 --- a/files/README/source/docs/flags.meta +++ b/docs/source/docs/flags.meta @@ -62,5 +62,6 @@ ${ --undefined panics and stops building site if any undefined variables are encountered --no-pandoc - don't call pandoc on source files - allows metaforge to run without pandoc installed + don't call pandoc on source files. allows metaforge to run without pandoc installed + --no-minify + don't minify resulting html diff --git a/files/README/source/docs/header.meta b/docs/source/docs/header.meta similarity index 88% rename from files/README/source/docs/header.meta rename to docs/source/docs/header.meta index 88a5351..60900a7 100644 --- a/files/README/source/docs/header.meta +++ b/docs/source/docs/header.meta @@ -37,11 +37,13 @@ normal definition blocks. - blank = **BOOL** - if true, stops parsing and returns an empty string - panic_default = **BOOL** - if true, panics on an undefined default pattern - panic_undefined = **BOOL** - if true, panics on an undefined variable or array +- source = **STRING** - change the the filetype of the source file +- filetype = **STRING** - change the filetype of the output file - equal_arrays = **BOOL** - if true, panics if arrays in the same pattern have different sizes +- minify = **BOOL** - toggles html minification +- pandoc = **BOOL** - toggles if pandoc is ran on this file to convert between filetypes, defaults to *true* in **source** dir, and *false* in **pattern** dir. ### source - ignore = **BOOL** - stops parsing and skips this file, useful for ignoring directories with scoped definitions -- source = **STRING** - change the the filetype of the source file -- filetype = **STRING** - change the filetype of the output file -- pandoc = **BOOL** - toggles if pandoc is ran on this file to convert between filetypes +- copy_only = **BOOL** - copys file or directory without processing anything diff --git a/files/README/source/docs/structure.meta b/docs/source/docs/structure.meta similarity index 99% rename from files/README/source/docs/structure.meta rename to docs/source/docs/structure.meta index b8c51dc..22a5bef 100644 --- a/files/README/source/docs/structure.meta +++ b/docs/source/docs/structure.meta @@ -22,6 +22,7 @@ files from the pattern directory the should generally contain html snippets, but can contain anything that you'd like to substitute. required directories are: + - source (site structure and contents) - pattern (patterns for expansion) - pattern/base (gets expanded to start building each pattern) diff --git a/files/README/source/docs/syntax.meta b/docs/source/docs/syntax.meta similarity index 93% rename from files/README/source/docs/syntax.meta rename to docs/source/docs/syntax.meta index 72deca7..1f55255 100644 --- a/files/README/source/docs/syntax.meta +++ b/docs/source/docs/syntax.meta @@ -63,8 +63,11 @@ anywhere in or across a line. foobaz } ## layout -- optional header definition block -- optional variable, array, pattern definition blocks + +*all sections are optional* + +- header definition block +- variable, array, pattern definition blocks - source ### example diff --git a/files/README/source/index.meta b/docs/source/index.meta similarity index 55% rename from files/README/source/index.meta rename to docs/source/index.meta index 8a2b159..2c13574 100644 --- a/files/README/source/index.meta +++ b/docs/source/index.meta @@ -6,13 +6,14 @@ this is the documentation for metaforge, generated by metaforge itself. currently it's unstyled html rendered by your browser, so it's pretty bare-bones. -open **files/README** in metaforge's repository and explore the source and pattern +open **docs/source** in metaforge's repository and explore the source and pattern files to get some examples of how this site works. -you can change anything in the **README** directory and see how it affects the site once +you can change anything in the **docs** directory and see how it affects the site once you rebuild it. ## contents + - [syntax](docs/syntax.html) - [definitions](docs/definitions.html) - [expansions](docs/expansions.html) @@ -20,6 +21,15 @@ you rebuild it. - [headers](docs/header.html) - [flags](docs/flags.html) +## new in this version + +- spaces are correctly preserved between directly adjacent substitutions +- include from source dir +- copy_only header directive +- pandoc header now works on pattern files +- html minification header and flags + ## versions +- 0.1.3: grammar and headers - 0.1.2: multithreading - 0.1.1: initial release diff --git a/files/test_site/pattern/test/expand_source.meta b/files/test_site/pattern/test/expand_source.meta new file mode 100644 index 0000000..c2d0d56 --- /dev/null +++ b/files/test_site/pattern/test/expand_source.meta @@ -0,0 +1 @@ +&{SOURCE.unit_tests.expand.source_expand} diff --git a/files/test_site/pattern/test/pandoc.meta b/files/test_site/pattern/test/pandoc.meta new file mode 100644 index 0000000..6d3869a --- /dev/null +++ b/files/test_site/pattern/test/pandoc.meta @@ -0,0 +1,3 @@ +#{ pandoc = true } + +# GOOD diff --git a/files/test_site/source/unit_tests/expand/source.meta b/files/test_site/source/unit_tests/expand/source.meta new file mode 100644 index 0000000..c97e3bc --- /dev/null +++ b/files/test_site/source/unit_tests/expand/source.meta @@ -0,0 +1,3 @@ +${ var = 'GOOD' } + +&{ test = 'expand_source' } diff --git a/files/test_site/source/unit_tests/expand/source_expand.meta b/files/test_site/source/unit_tests/expand/source_expand.meta new file mode 100644 index 0000000..c290b5a --- /dev/null +++ b/files/test_site/source/unit_tests/expand/source_expand.meta @@ -0,0 +1,2 @@ +GOOD +${var} diff --git a/files/test_site/source/unit_tests/expand/spaces.meta b/files/test_site/source/unit_tests/expand/spaces.meta new file mode 100644 index 0000000..073cf92 --- /dev/null +++ b/files/test_site/source/unit_tests/expand/spaces.meta @@ -0,0 +1,6 @@ +${ + var1 = 'GOOD' + var2 = 'GOOD' +} + +${var1} ${var2} diff --git a/files/test_site/source/unit_tests/header/copy.meta b/files/test_site/source/unit_tests/header/copy.meta new file mode 100644 index 0000000..fa04ce3 --- /dev/null +++ b/files/test_site/source/unit_tests/header/copy.meta @@ -0,0 +1,3 @@ +#{ copy_only = true } + +variable: ${this} should get copied verbatim diff --git a/files/test_site/source/unit_tests/header/expandoc.meta b/files/test_site/source/unit_tests/header/expandoc.meta new file mode 100644 index 0000000..aa6c241 --- /dev/null +++ b/files/test_site/source/unit_tests/header/expandoc.meta @@ -0,0 +1,3 @@ +&{test = 'pandoc'} + +source diff --git a/src/lib.rs b/src/lib.rs index 1f34105..696d5eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ pub fn single_file(opts: &Options) -> Result { })), }?; - let file = parse_string(source, opts)?; + let mut file = parse_string(source, opts)?; Ok(file.construct()?) } diff --git a/src/metafile.rs b/src/metafile.rs index c23fa8b..b3c1f5f 100644 --- a/src/metafile.rs +++ b/src/metafile.rs @@ -11,6 +11,8 @@ pub use scope::*; #[cfg(test)] mod tests; +use std::fmt::Display; + #[derive(Debug, Clone, PartialEq)] pub enum Src { Str(String), @@ -20,27 +22,29 @@ pub enum Src { } impl Src { - pub fn to_var(var: impl ToString) -> Self { + pub fn to_var(var: impl Display) -> Self { Src::Var(var.to_string()) } - pub fn to_arr(arr: impl ToString) -> Self { + pub fn to_arr(arr: impl Display) -> Self { Src::Arr(arr.to_string()) } - pub fn to_pat(pat: impl ToString) -> Self { + pub fn to_pat(pat: impl Display) -> Self { Src::Pat(pat.to_string()) } - pub fn to_str(str: impl ToString) -> Self { + pub fn to_str(str: impl Display) -> Self { Src::Str(str.to_string()) } } -impl ToString for Src { - fn to_string(&self) -> String { - match self { +impl Display for Src { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { Src::Var(x) | Src::Arr(x) | Src::Pat(x) | Src::Str(x) => x.to_string(), - } + }; + + write!(f, "{str}") } } diff --git a/src/metafile/dir/node.rs b/src/metafile/dir/node.rs index e57630e..d9b2a05 100644 --- a/src/metafile/dir/node.rs +++ b/src/metafile/dir/node.rs @@ -1,9 +1,26 @@ use crate::{error::*, Options}; use eyre::Result; +use minify_html::{minify, Cfg}; use std::{fs, path::PathBuf}; use super::*; +const HTML_CFG: Cfg = Cfg { + do_not_minify_doctype: false, + ensure_spec_compliant_unquoted_attribute_values: false, + keep_closing_tags: true, + keep_html_and_head_opening_tags: true, + keep_spaces_between_attributes: false, + keep_comments: false, + minify_css: true, + minify_css_level_1: false, + minify_css_level_2: true, + minify_css_level_3: false, + minify_js: true, + remove_bangs: true, + remove_processing_instructions: true, +}; + impl<'a> DirNode<'a> { pub fn build(path: PathBuf, opts: &'a Options) -> Result { assert!(path.is_dir() && path.exists()); @@ -43,6 +60,12 @@ impl<'a> DirNode<'a> { for f in fs::read_dir(&self.path)? { let file = f?.path(); + if self.global.header.copy_only { + let dest = self.global.dest()?; + fs::copy(file, &dest.parent().unwrap_or(&self.opts.build))?; + continue; + } + if file.is_dir() { let dir = DirNode::build(file, self.opts)?; self.dirs.push(dir); @@ -63,7 +86,11 @@ impl<'a> DirNode<'a> { file.merge(&self.global); match file.construct() { Ok(str) => { - fs::write(file.dest()?, str)?; + if file.header.minify && self.opts.minify { + fs::write(file.dest()?, minify(str.as_bytes(), &HTML_CFG))?; + } else { + fs::write(file.dest()?, str)?; + } } Err(e) => { // print a line to stderr about failure but continue with other files diff --git a/src/metafile/file.rs b/src/metafile/file.rs index b664b09..b865709 100644 --- a/src/metafile/file.rs +++ b/src/metafile/file.rs @@ -10,6 +10,7 @@ use pandoc::{InputFormat, InputKind, OutputFormat, OutputKind, Pandoc}; use std::{collections::HashMap, path::PathBuf}; use super::*; + #[derive(Debug, Clone)] pub struct MetaFile<'a> { pub opts: &'a Options, @@ -54,7 +55,7 @@ impl<'a> MetaFile<'a> { Ok(metafile) } - pub fn construct(&self) -> Result> { + pub fn construct(&mut self) -> Result> { log!(self.opts, format!("building {}", self.path.display()), 1); if self.header.blank { @@ -63,7 +64,19 @@ impl<'a> MetaFile<'a> { return Err(Box::new(MetaError::Ignored)); } - let html = self.to_html().map_err(MetaError::from)?; + if self.header.copy_only { + let dest = self.dest().map_err(MetaError::from)?; + let source: String = self.source.iter().map(|s| s.to_string()).collect(); + std::fs::write(dest, source).unwrap(); + return Err(Box::new(MetaError::Ignored)); + } + + let src_str: String; + if self.header.pandoc.map_or(true, |x| x) { + src_str = self.pandoc().map_err(MetaError::from)?; + } else { + src_str = self.get_source().map_err(MetaError::from)?; + } let pattern = self.get_pattern("base").map_err(MetaError::from)?; let mut base = parse_string(pattern, self.opts).map_err(|e| MetaError::ParserError { @@ -72,7 +85,16 @@ impl<'a> MetaFile<'a> { })?; base.merge(self); - base.patterns.insert(Scope::into_global("SOURCE"), html); + base.patterns + .insert(Scope::create_global("SOURCE"), src_str); + let mut base_path = self.opts.pattern.join("base").join( + self.patterns + .get(&Scope::create_global("base")) + .unwrap_or(&"default".into()), + ); + + base_path.set_extension("meta"); + base.path = base_path; let output = base.get_source().map_err(MetaError::from)?; diff --git a/src/metafile/file/arrays.rs b/src/metafile/file/arrays.rs index 122e8b1..1b38c0f 100644 --- a/src/metafile/file/arrays.rs +++ b/src/metafile/file/arrays.rs @@ -29,15 +29,15 @@ impl<'a> MetaFile<'a> { let value = if let Some(val) = self.arrays.get(&name_key) { &val[..] - } else if let Some(val) = self.arrays.get(&name_key.to_global()) { + } else if let Some(val) = self.arrays.get(&name_key.global()) { &val[..] } else if let Some(val) = self.arrays.get(&class_key) { &val[..] - } else if let Some(val) = self.arrays.get(&class_key.to_global()) { + } else if let Some(val) = self.arrays.get(&class_key.global()) { &val[..] - } else if let Some(val) = self.arrays.get(&Scope::into_global(key)) { + } else if let Some(val) = self.arrays.get(&Scope::create_global(key)) { &val[..] - } else if let Some(val) = self.arrays.get(&Scope::into_local(key)) { + } else if let Some(val) = self.arrays.get(&Scope::create_local(key)) { &val[..] } else if self.opts.undefined { panic!( diff --git a/src/metafile/file/patterns.rs b/src/metafile/file/patterns.rs index 1df5844..304254f 100644 --- a/src/metafile/file/patterns.rs +++ b/src/metafile/file/patterns.rs @@ -5,22 +5,25 @@ impl<'a> MetaFile<'a> { log!(self.opts, format!("expanding {key}"), 2); // SOURCE is already expanded in the initial construct() call if key == "SOURCE" { - if let Some(source) = self.patterns.get(&Scope::into_global("SOURCE")) { + if let Some(source) = self.patterns.get(&Scope::create_global("SOURCE")) { return Ok(source.to_string()); } else { return Ok(String::new()); } } - let mut filename = if let Some(name) = self.patterns.get(&Scope::into_local(key)) { + let is_source = key.split('.').next().unwrap_or("") == "SOURCE"; + + let mut filename = if let Some(name) = self.patterns.get(&Scope::create_local(key)) { Ok(name.to_string()) - } else if let Some(name) = self.patterns.get(&Scope::into_global(key)) { + } else if let Some(name) = self.patterns.get(&Scope::create_global(key)) { Ok(name.to_string()) } else if self .opts .pattern .join(key.replace(".", "/") + ".meta") .exists() + || is_source { Ok(String::new()) } else if self.header.panic_default { @@ -61,14 +64,24 @@ impl<'a> MetaFile<'a> { } let pattern_path = key.replace('.', "/") + "/" + &filename; - let mut path = self.opts.pattern.join(pattern_path); - path.set_extension("meta"); + let mut path = if is_source { + let pattern_path = pattern_path.replace("SOURCE/", ""); + self.opts.source.join(pattern_path) + } else { + self.opts.pattern.join(pattern_path) + }; + + path.set_extension("meta"); let mut pattern = MetaFile::build(path, self.opts)?; // copy over maps for expanding contained variables pattern.merge(self); - pattern.get_source() + if pattern.header.pandoc.unwrap_or(false) || is_source { + pattern.pandoc() + } else { + pattern.get_source() + } } } diff --git a/src/metafile/file/source.rs b/src/metafile/file/source.rs index 6ffad58..3aca3b4 100644 --- a/src/metafile/file/source.rs +++ b/src/metafile/file/source.rs @@ -1,10 +1,10 @@ use super::*; impl<'a> MetaFile<'a> { - pub fn to_html(&self) -> Result { + pub fn pandoc(&mut self) -> Result { let string = self.get_source()?; - if self.opts.no_pandoc || !self.header.pandoc || string.is_empty() { + if self.opts.no_pandoc || string.is_empty() { return Ok(string); } @@ -28,6 +28,7 @@ impl<'a> MetaFile<'a> { .set_output_format(output, vec![]); if let pandoc::PandocOutput::ToBuffer(s) = pandoc.execute()? { + self.header.pandoc = Some(false); Ok(s) } else { Err(MetaError::Pandoc { file: self.name()? }.into()) diff --git a/src/metafile/file/variables.rs b/src/metafile/file/variables.rs index 033f38c..08f09f8 100644 --- a/src/metafile/file/variables.rs +++ b/src/metafile/file/variables.rs @@ -12,13 +12,13 @@ impl<'a> MetaFile<'a> { 2 ); let long_key = self.name()? + "." + &key.to_string(); - if let Some(val) = self.variables.get(&Scope::into_local(&long_key)) { + if let Some(val) = self.variables.get(&Scope::create_local(&long_key)) { Ok(val.clone()) - } else if let Some(val) = self.variables.get(&Scope::into_global(&long_key)) { + } else if let Some(val) = self.variables.get(&Scope::create_global(&long_key)) { Ok(val.clone()) - } else if let Some(val) = self.variables.get(&Scope::into_local(key)) { + } else if let Some(val) = self.variables.get(&Scope::create_local(key)) { Ok(val.clone()) - } else if let Some(val) = self.variables.get(&Scope::into_global(key)) { + } else if let Some(val) = self.variables.get(&Scope::create_global(key)) { Ok(val.clone()) } else if self.opts.undefined || self.header.panic_undefined { return Err(MetaError::UndefinedExpand { diff --git a/src/metafile/header.rs b/src/metafile/header.rs index fdaed0a..a895ac3 100644 --- a/src/metafile/header.rs +++ b/src/metafile/header.rs @@ -8,8 +8,10 @@ pub struct Header { pub equal_arrays: bool, pub filetype: String, pub source: String, - pub pandoc: bool, + pub pandoc: Option, pub ignore: bool, + pub copy_only: bool, + pub minify: bool, } impl Header { @@ -21,8 +23,10 @@ impl Header { equal_arrays: false, filetype: String::from("html"), source: String::from("markdown"), - pandoc: true, + pandoc: None, ignore: false, + copy_only: false, + minify: true, } } } @@ -36,10 +40,12 @@ impl From> for Header { "panic_default" => header.panic_default = val == "true", "panic_undefined" => header.panic_undefined = val == "true", "equal_arrays" => header.equal_arrays = val == "true", - "pandoc" => header.pandoc = val == "true", + "pandoc" => header.pandoc = Some(val == "true"), "filetype" => header.filetype = val.to_string(), "source" => header.source = val.to_string(), "ignore" => header.ignore = val == "true", + "copy_only" => header.copy_only = val == "true", + "minify" => header.copy_only = val == "true", _ => continue, } } diff --git a/src/metafile/scope.rs b/src/metafile/scope.rs index 8e23b1d..c1187c7 100644 --- a/src/metafile/scope.rs +++ b/src/metafile/scope.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + #[derive(Debug, Clone, Eq, Hash, PartialEq)] pub enum Scope { Local(String), @@ -5,11 +7,11 @@ pub enum Scope { } impl Scope { - pub fn into_local(str: impl ToString) -> Scope { + pub fn create_local(str: impl Display) -> Scope { Scope::Local(str.to_string()) } - pub fn into_global(str: impl ToString) -> Scope { + pub fn create_global(str: impl ToString) -> Scope { Scope::Global(str.to_string()) } @@ -27,19 +29,21 @@ impl Scope { } } - pub fn to_local(&self) -> Scope { + pub fn local(&self) -> Scope { Scope::Local(self.to_string()) } - pub fn to_global(&self) -> Scope { + pub fn global(&self) -> Scope { Scope::Global(self.to_string()) } } -impl ToString for Scope { - fn to_string(&self) -> String { - match self { +impl Display for Scope { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { Scope::Local(x) | Scope::Global(x) => x.to_string(), - } + }; + + write!(f, "{str}") } } diff --git a/src/options.rs b/src/options.rs index 704d2cb..0b021e7 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; #[derive(Parser, Debug)] #[command(author = "huck boles")] -#[command(version = "0.1.2")] +#[command(version = "0.1.3")] #[command(about = "customizable template driven static site generator")] #[command(long_about = None)] pub struct Opts { @@ -53,6 +53,9 @@ pub struct Opts { /// don't call pandoc on source files #[arg(long, default_value_t = false)] pub no_pandoc: bool, + /// don't minify resulting html + #[arg(long, default_value_t = false)] + pub no_minify: bool, } #[derive(Debug, Clone, Default)] @@ -72,6 +75,7 @@ pub struct Options { pub clean: bool, pub no_pandoc: bool, pub new: bool, + pub minify: bool, } impl Options { @@ -92,6 +96,7 @@ impl Options { clean: false, no_pandoc: false, new: false, + minify: true, } } } @@ -109,6 +114,7 @@ impl TryFrom for Options { opts.no_pandoc = value.no_pandoc; opts.new = value.new; opts.parallel = value.parallel; + opts.minify = !value.no_minify; opts.root = if let Some(root) = value.root.as_deref() { PathBuf::from(root).canonicalize() diff --git a/src/parser.rs b/src/parser.rs index 7705158..d30dea5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -11,7 +11,7 @@ use source::*; #[cfg(test)] mod tests; -use crate::{log, Header, MetaError, MetaFile, Options}; +use crate::{Header, MetaError, MetaFile, Options}; use eyre::Result; use pest::{ iterators::{Pair, Pairs}, @@ -23,8 +23,6 @@ use pest::{ pub struct MetaParser; pub fn parse_string(file: String, opts: &Options) -> Result { - log!(opts, "parsing file", 3); - let pair = MetaParser::parse(Rule::file, &file)?.next().unwrap(); let mut meta_file = MetaFile::new(opts); diff --git a/src/parser/array.rs b/src/parser/array.rs index 8358f58..30b2979 100644 --- a/src/parser/array.rs +++ b/src/parser/array.rs @@ -40,9 +40,9 @@ fn parse_assign_array(pair: Pair) -> Result<(Scope, Vec)> { } if global { - Ok((Scope::into_global(key), val)) + Ok((Scope::create_global(key), val)) } else { - Ok((Scope::into_local(key), val)) + Ok((Scope::create_local(key), val)) } } diff --git a/src/parser/def_block.rs b/src/parser/def_block.rs index 26333ef..080d1d4 100644 --- a/src/parser/def_block.rs +++ b/src/parser/def_block.rs @@ -55,8 +55,8 @@ fn parse_assign(pair: Pair) -> Result<(Scope, &str)> { } if global { - Ok((Scope::into_global(key), val)) + Ok((Scope::create_global(key), val)) } else { - Ok((Scope::into_local(key), val)) + Ok((Scope::create_local(key), val)) } } diff --git a/src/parser/meta.pest b/src/parser/meta.pest index a4aa0d7..1116c97 100644 --- a/src/parser/meta.pest +++ b/src/parser/meta.pest @@ -17,10 +17,10 @@ char = _{ array = _{ "[" ~ "]" - | "[" - ~ WHITESPACE* - ~ string - ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ string)* + | "[" + ~ WHITESPACE* + ~ string + ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ string)* ~ WHITESPACE* ~ ","? ~ WHITESPACE* ~ "]" } @@ -46,9 +46,9 @@ substitution = _{ sigil ~ key ~ "}" } var_sub = { &("$") ~ substitution } arr_sub = { &("@") ~ substitution } pat_sub = { &("&") ~ substitution } -identifier = _{ var_sub | pat_sub | arr_sub } +identifier = _{ var_sub | pat_sub | arr_sub | COMMENT} -source = { (identifier | char_seq)* } +source = ${ (identifier | char_seq)* } file = { SOI ~ header? ~ definition* ~ source? ~ EOI diff --git a/src/tests.rs b/src/tests.rs index 26fb177..8564ce7 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{MetaFile, Options}; +use crate::{MetaError, MetaFile, Options}; use eyre::Result; use std::{fs, path::PathBuf}; @@ -17,8 +17,18 @@ macro_rules! unit_test ( let test_dir = opts.source.join("unit_tests"); let mut path = test_dir.join($file); path.set_extension("meta"); - let file = MetaFile::build(path, &opts)?; - assert_eq!(file.construct()?, $test); + let mut file = MetaFile::build(path, &opts)?; + + let str = match file.construct() { + Ok(f) => f, + Err(e) => match *e { + MetaError::Ignored => return Ok(()), + e => return Err(e.into()) + } + + }; + + assert_eq!(str, $test); Ok(()) } }; @@ -40,65 +50,69 @@ macro_rules! panic_test ( let test_dir = opts.source.join("unit_tests"); let mut path = test_dir.join($file); path.set_extension("meta"); - let file = MetaFile::build(path, &opts).unwrap(); + let mut file = MetaFile::build(path, &opts).unwrap(); assert_eq!(file.construct().unwrap(), $test); } }; ); unit_test!(blank_pattern, "blank/blank_pattern", ""); -unit_test!(blank_variable, "blank/blank_variable", "\n\n"); -unit_test!(blank_array, "blank/blank_array", "\n\n"); -unit_test!(blank_comment, "blank/comment", "\n\n"); +unit_test!( + blank_variable, + "blank/blank_variable", + "\n\n\n\n" +); +unit_test!(blank_array, "blank/blank_array", "\n\n\n\n"); +unit_test!(blank_comment, "blank/comment", "\n\n\n\n\n"); unit_test!( inline_comment, "blank/inline_comment", - "\n

inline comment

\n\n" + "\n

inline comment

\n\n\n\n\n" ); unit_test!( expand_var_in_src, "expand/variable_in_source", - "\n

GOOD

\n\n" + "\n

GOOD

\n\n\n\n\n" ); unit_test!( expand_var_in_pat, "expand/variable_in_pattern", - "\nGOOD\n" + "\nGOOD\n\n\n\n" ); unit_test!( expand_arr_in_src, "expand/array_in_source", - "\n

12345

\n\n" + "\n

1 2 3 4 5

\n\n\n\n\n" ); unit_test!( expand_arr_in_pat, "expand/array_in_pattern", - "\n12345\n" + "\n1\n2\n3\n4\n5\n\n\n\n" ); unit_test!( expand_pat_in_src, "expand/pattern_in_source", - "

GOOD

\n" + "

GOOD

\n\n" ); unit_test!( expand_pat_in_pat, "expand/pattern_in_pattern", - "\nGOOD\nGOOD\n\n" + "\nGOOD\nGOOD\n\n\n\n\n" ); unit_test!( override_var, "override/variable", - "\n

GOOD

\n\n" + "\n

GOOD

\n\n\n\n\n" ); unit_test!( override_pat, "override/pattern", - "\nGOOD\nGOOD\n\n" + "\nGOOD\n GOOD\n\n\n\n\n" ); unit_test!( header_no_pandoc, "header/pandoc", - "# This should not become html\n" + "# This should not become html\n\n" ); unit_test!(header_blank, "header/blank", ""); @@ -106,13 +120,37 @@ unit_test!(header_blank, "header/blank", ""); unit_test!( pat_file, "expand/file.meta", - "\n

GOOD

\n\n" + "\n

GOOD

\n\n\n\n\n" ); unit_test!( direct_call, "expand/direct_call", - "\n

abcd

\n\n" + "\n

a b c d

\n\n\n\n\n" +); + +unit_test!( + expand_spaces, + "expand/spaces", + "\n

GOOD GOOD

\n\n\n\n\n" +); + +unit_test!( + copy_header, + "header/copy", + r#"variable: ${this} should get copied verbatim"# +); + +unit_test!( + expandoc, + "header/expandoc", + "\n

GOOD

\n\n\n\n" +); + +unit_test!( + include_source, + "expand/source", + "\n

GOOD GOOD

\n\n\n\n\n" ); panic_test!(ignore, "ignore.meta", ""); @@ -157,12 +195,12 @@ fn test_global() -> Result<()> { assert_eq!( fs::read_to_string(dir.join("build/unit_tests/global/pattern.html"))?, - "

GOOD GOOD

\n" + "

GOOD

GOOD

" ); assert_eq!( fs::read_to_string(dir.join("build/unit_tests/global/variable.html"))?, - "

GOODGOOD

\n" + "

GOOD GOOD

" ); Ok(()) diff --git a/tests/readme.rs b/tests/readme.rs index 01279d7..543bf2e 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -3,7 +3,7 @@ use eyre::Result; #[test] #[ignore = "generates README site"] fn readme() -> Result<()> { - let dir = std::path::PathBuf::from("files/README") + let dir = std::path::PathBuf::from("docs") .canonicalize() .unwrap();