From b8eba1eb3571c30752e467dc5a7d3c9100e7cbae Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 10 Oct 2026 13:08:01 +0200 Subject: [PATCH] Ship en_US, sv_SE, misc, geo/SE and geo/US as Go packages exporting FS, listed by data.Modules(), and fail a bare New naming WithDataFS --- AGENTS.md | 2 +- CHANGELOG.md | 9 ++- README.md | 53 +++++++----- cmd/fejkdata/main.go | 9 ++- data-import/currency.py | 4 +- data-import/geo-se.py | 2 +- data-import/geo-us.py | 2 +- data-import/geo.py | 2 +- data-import/httpmethod.py | 4 +- data-import/httpstatus.py | 4 +- data-import/language.py | 4 +- data-import/loglevel.py | 4 +- data-import/mimetype.py | 4 +- data-import/names-se.py | 4 +- data-import/names-us.py | 4 +- data-import/port.py | 4 +- data-import/protocol.py | 4 +- data-import/territory.py | 4 +- data-import/timezone.py | 6 +- data-import/tld.py | 4 +- data-import/useragent.py | 4 +- data.go | 15 ++-- data/.fejkdata.json | 80 ------------------- data/en_US/.fejkdata.json | 27 +++++++ data/en_US/{ => en_US}/address.json | 0 data/en_US/{ => en_US}/color.json | 0 data/en_US/{ => en_US}/company.json | 0 data/en_US/{ => en_US}/date.json | 0 data/en_US/{ => en_US}/email.json | 0 data/en_US/{ => en_US}/first-name.json | 0 data/en_US/{ => en_US}/first-name.tsv | 0 data/en_US/{ => en_US}/ip.json | 0 data/en_US/{ => en_US}/itin.json | 0 data/en_US/{ => en_US}/last-name.json | 0 data/en_US/{ => en_US}/last-name.tsv | 0 data/en_US/{ => en_US}/person.json | 0 data/en_US/{ => en_US}/phone-exchange.json | 0 data/en_US/{ => en_US}/phone.json | 0 data/en_US/{ => en_US}/price.json | 0 data/en_US/{ => en_US}/sentence.json | 0 data/en_US/{ => en_US}/sex.json | 0 data/en_US/{ => en_US}/sex.tsv | 0 data/en_US/{ => en_US}/ssn.json | 0 data/en_US/{ => en_US}/time.json | 0 data/en_US/{ => en_US}/title.json | 0 data/en_US/{ => en_US}/title.tsv | 0 data/en_US/{ => en_US}/url.json | 0 data/en_US/{ => en_US}/username.json | 0 data/en_US/{ => en_US}/version.json | 0 data/en_US/{ => en_US}/word.json | 0 data/en_US/fs.go | 9 +++ data/geo/SE/.fejkdata.json | 10 +++ data/geo/SE/fs.go | 9 +++ data/geo/SE/{ => geo/SE}/address.json | 0 data/geo/SE/{ => geo/SE}/locality.json | 0 data/geo/SE/{ => geo/SE}/locality.tsv | 0 data/geo/SE/{ => geo/SE}/municipality.json | 0 data/geo/SE/{ => geo/SE}/municipality.tsv | 0 data/geo/SE/{ => geo/SE}/postal-code.json | 0 data/geo/SE/{ => geo/SE}/postal-code.tsv | 0 data/geo/SE/{ => geo/SE}/region.json | 0 data/geo/SE/{ => geo/SE}/region.tsv | 0 data/geo/SE/{ => geo/SE}/street.json | 0 data/geo/SE/{ => geo/SE}/street.tsv | 0 data/geo/US/.fejkdata.json | 10 +++ data/geo/US/fs.go | 9 +++ data/geo/US/{ => geo/US}/address.json | 0 data/geo/US/{ => geo/US}/locality.json | 0 data/geo/US/{ => geo/US}/locality.tsv | 0 data/geo/US/{ => geo/US}/municipality.json | 0 data/geo/US/{ => geo/US}/municipality.tsv | 0 data/geo/US/{ => geo/US}/postal-code.json | 0 data/geo/US/{ => geo/US}/postal-code.tsv | 0 data/geo/US/{ => geo/US}/region.json | 0 data/geo/US/{ => geo/US}/region.tsv | 0 data/geo/US/{ => geo/US}/street.json | 0 data/geo/US/{ => geo/US}/street.tsv | 0 data/misc/.fejkdata.json | 24 ++++++ data/misc/fs.go | 9 +++ data/misc/{ => misc}/car.json | 0 data/misc/{ => misc}/car.tsv | 0 data/misc/{ => misc}/coordinate.json | 0 data/misc/{ => misc}/creditcard.json | 0 data/misc/{ => misc}/currency.json | 0 data/misc/{ => misc}/currency.tsv | 0 data/misc/{ => misc}/datetime.json | 0 data/misc/{ => misc}/emoji.json | 0 data/misc/{ => misc}/httpmethod.json | 0 data/misc/{ => misc}/httpmethod.tsv | 0 data/misc/{ => misc}/httpstatus.json | 0 data/misc/{ => misc}/httpstatus.tsv | 0 data/misc/{ => misc}/language.json | 0 data/misc/{ => misc}/language.tsv | 0 data/misc/{ => misc}/loglevel.json | 0 data/misc/{ => misc}/loglevel.tsv | 0 data/misc/{ => misc}/mac.json | 0 data/misc/{ => misc}/mimetype.json | 0 data/misc/{ => misc}/mimetype.tsv | 0 data/misc/{ => misc}/objectid.json | 0 data/misc/{ => misc}/port.json | 0 data/misc/{ => misc}/port.tsv | 0 data/misc/{ => misc}/protocol.json | 0 data/misc/{ => misc}/protocol.tsv | 0 data/misc/{ => misc}/territory.json | 0 data/misc/{ => misc}/territory.tsv | 0 data/misc/{ => misc}/timezone.json | 0 data/misc/{ => misc}/timezone.tsv | 0 data/misc/{ => misc}/tld.json | 0 data/misc/{ => misc}/tld.tsv | 0 data/misc/{ => misc}/useragent.json | 0 data/misc/{ => misc}/useragent.tsv | 0 data/misc/{ => misc}/uuid.json | 0 data/modules.go | 19 +++++ data/sv_SE/.fejkdata.json | 27 +++++++ data/sv_SE/fs.go | 9 +++ data/sv_SE/{ => sv_SE}/address.json | 0 data/sv_SE/{ => sv_SE}/birth-number.json | 0 data/sv_SE/{ => sv_SE}/birth-number.tsv | 0 data/sv_SE/{ => sv_SE}/color.json | 0 data/sv_SE/{ => sv_SE}/company.json | 0 data/sv_SE/{ => sv_SE}/date.json | 0 data/sv_SE/{ => sv_SE}/email.json | 0 data/sv_SE/{ => sv_SE}/first-name.json | 0 data/sv_SE/{ => sv_SE}/first-name.tsv | 0 data/sv_SE/{ => sv_SE}/ip.json | 0 data/sv_SE/{ => sv_SE}/last-name.json | 0 data/sv_SE/{ => sv_SE}/last-name.tsv | 0 data/sv_SE/{ => sv_SE}/person.json | 0 data/sv_SE/{ => sv_SE}/personnummer.json | 0 data/sv_SE/{ => sv_SE}/phone.json | 0 data/sv_SE/{ => sv_SE}/price.json | 0 data/sv_SE/{ => sv_SE}/samordningsnummer.json | 0 data/sv_SE/{ => sv_SE}/sentence.json | 0 data/sv_SE/{ => sv_SE}/sex.json | 0 data/sv_SE/{ => sv_SE}/sex.tsv | 0 data/sv_SE/{ => sv_SE}/time.json | 0 data/sv_SE/{ => sv_SE}/title.json | 0 data/sv_SE/{ => sv_SE}/title.tsv | 0 data/sv_SE/{ => sv_SE}/url.json | 0 data/sv_SE/{ => sv_SE}/username.json | 0 data/sv_SE/{ => sv_SE}/version.json | 0 data/sv_SE/{ => sv_SE}/word.json | 0 doc.go | 9 ++- docs/decisions.md | 25 +++--- fejkdata.go | 58 +++++++------- index.go | 10 ++- internal/datafiles/manifest.go | 41 ++++++++-- node.go | 13 +++ resolve.go | 26 ++++-- shipped.go | 14 ---- todo.md | 19 +---- 151 files changed, 377 insertions(+), 232 deletions(-) delete mode 100644 data/.fejkdata.json create mode 100644 data/en_US/.fejkdata.json rename data/en_US/{ => en_US}/address.json (100%) rename data/en_US/{ => en_US}/color.json (100%) rename data/en_US/{ => en_US}/company.json (100%) rename data/en_US/{ => en_US}/date.json (100%) rename data/en_US/{ => en_US}/email.json (100%) rename data/en_US/{ => en_US}/first-name.json (100%) rename data/en_US/{ => en_US}/first-name.tsv (100%) rename data/en_US/{ => en_US}/ip.json (100%) rename data/en_US/{ => en_US}/itin.json (100%) rename data/en_US/{ => en_US}/last-name.json (100%) rename data/en_US/{ => en_US}/last-name.tsv (100%) rename data/en_US/{ => en_US}/person.json (100%) rename data/en_US/{ => en_US}/phone-exchange.json (100%) rename data/en_US/{ => en_US}/phone.json (100%) rename data/en_US/{ => en_US}/price.json (100%) rename data/en_US/{ => en_US}/sentence.json (100%) rename data/en_US/{ => en_US}/sex.json (100%) rename data/en_US/{ => en_US}/sex.tsv (100%) rename data/en_US/{ => en_US}/ssn.json (100%) rename data/en_US/{ => en_US}/time.json (100%) rename data/en_US/{ => en_US}/title.json (100%) rename data/en_US/{ => en_US}/title.tsv (100%) rename data/en_US/{ => en_US}/url.json (100%) rename data/en_US/{ => en_US}/username.json (100%) rename data/en_US/{ => en_US}/version.json (100%) rename data/en_US/{ => en_US}/word.json (100%) create mode 100644 data/en_US/fs.go create mode 100644 data/geo/SE/.fejkdata.json create mode 100644 data/geo/SE/fs.go rename data/geo/SE/{ => geo/SE}/address.json (100%) rename data/geo/SE/{ => geo/SE}/locality.json (100%) rename data/geo/SE/{ => geo/SE}/locality.tsv (100%) rename data/geo/SE/{ => geo/SE}/municipality.json (100%) rename data/geo/SE/{ => geo/SE}/municipality.tsv (100%) rename data/geo/SE/{ => geo/SE}/postal-code.json (100%) rename data/geo/SE/{ => geo/SE}/postal-code.tsv (100%) rename data/geo/SE/{ => geo/SE}/region.json (100%) rename data/geo/SE/{ => geo/SE}/region.tsv (100%) rename data/geo/SE/{ => geo/SE}/street.json (100%) rename data/geo/SE/{ => geo/SE}/street.tsv (100%) create mode 100644 data/geo/US/.fejkdata.json create mode 100644 data/geo/US/fs.go rename data/geo/US/{ => geo/US}/address.json (100%) rename data/geo/US/{ => geo/US}/locality.json (100%) rename data/geo/US/{ => geo/US}/locality.tsv (100%) rename data/geo/US/{ => geo/US}/municipality.json (100%) rename data/geo/US/{ => geo/US}/municipality.tsv (100%) rename data/geo/US/{ => geo/US}/postal-code.json (100%) rename data/geo/US/{ => geo/US}/postal-code.tsv (100%) rename data/geo/US/{ => geo/US}/region.json (100%) rename data/geo/US/{ => geo/US}/region.tsv (100%) rename data/geo/US/{ => geo/US}/street.json (100%) rename data/geo/US/{ => geo/US}/street.tsv (100%) create mode 100644 data/misc/.fejkdata.json create mode 100644 data/misc/fs.go rename data/misc/{ => misc}/car.json (100%) rename data/misc/{ => misc}/car.tsv (100%) rename data/misc/{ => misc}/coordinate.json (100%) rename data/misc/{ => misc}/creditcard.json (100%) rename data/misc/{ => misc}/currency.json (100%) rename data/misc/{ => misc}/currency.tsv (100%) rename data/misc/{ => misc}/datetime.json (100%) rename data/misc/{ => misc}/emoji.json (100%) rename data/misc/{ => misc}/httpmethod.json (100%) rename data/misc/{ => misc}/httpmethod.tsv (100%) rename data/misc/{ => misc}/httpstatus.json (100%) rename data/misc/{ => misc}/httpstatus.tsv (100%) rename data/misc/{ => misc}/language.json (100%) rename data/misc/{ => misc}/language.tsv (100%) rename data/misc/{ => misc}/loglevel.json (100%) rename data/misc/{ => misc}/loglevel.tsv (100%) rename data/misc/{ => misc}/mac.json (100%) rename data/misc/{ => misc}/mimetype.json (100%) rename data/misc/{ => misc}/mimetype.tsv (100%) rename data/misc/{ => misc}/objectid.json (100%) rename data/misc/{ => misc}/port.json (100%) rename data/misc/{ => misc}/port.tsv (100%) rename data/misc/{ => misc}/protocol.json (100%) rename data/misc/{ => misc}/protocol.tsv (100%) rename data/misc/{ => misc}/territory.json (100%) rename data/misc/{ => misc}/territory.tsv (100%) rename data/misc/{ => misc}/timezone.json (100%) rename data/misc/{ => misc}/timezone.tsv (100%) rename data/misc/{ => misc}/tld.json (100%) rename data/misc/{ => misc}/tld.tsv (100%) rename data/misc/{ => misc}/useragent.json (100%) rename data/misc/{ => misc}/useragent.tsv (100%) rename data/misc/{ => misc}/uuid.json (100%) create mode 100644 data/modules.go create mode 100644 data/sv_SE/.fejkdata.json create mode 100644 data/sv_SE/fs.go rename data/sv_SE/{ => sv_SE}/address.json (100%) rename data/sv_SE/{ => sv_SE}/birth-number.json (100%) rename data/sv_SE/{ => sv_SE}/birth-number.tsv (100%) rename data/sv_SE/{ => sv_SE}/color.json (100%) rename data/sv_SE/{ => sv_SE}/company.json (100%) rename data/sv_SE/{ => sv_SE}/date.json (100%) rename data/sv_SE/{ => sv_SE}/email.json (100%) rename data/sv_SE/{ => sv_SE}/first-name.json (100%) rename data/sv_SE/{ => sv_SE}/first-name.tsv (100%) rename data/sv_SE/{ => sv_SE}/ip.json (100%) rename data/sv_SE/{ => sv_SE}/last-name.json (100%) rename data/sv_SE/{ => sv_SE}/last-name.tsv (100%) rename data/sv_SE/{ => sv_SE}/person.json (100%) rename data/sv_SE/{ => sv_SE}/personnummer.json (100%) rename data/sv_SE/{ => sv_SE}/phone.json (100%) rename data/sv_SE/{ => sv_SE}/price.json (100%) rename data/sv_SE/{ => sv_SE}/samordningsnummer.json (100%) rename data/sv_SE/{ => sv_SE}/sentence.json (100%) rename data/sv_SE/{ => sv_SE}/sex.json (100%) rename data/sv_SE/{ => sv_SE}/sex.tsv (100%) rename data/sv_SE/{ => sv_SE}/time.json (100%) rename data/sv_SE/{ => sv_SE}/title.json (100%) rename data/sv_SE/{ => sv_SE}/title.tsv (100%) rename data/sv_SE/{ => sv_SE}/url.json (100%) rename data/sv_SE/{ => sv_SE}/username.json (100%) rename data/sv_SE/{ => sv_SE}/version.json (100%) rename data/sv_SE/{ => sv_SE}/word.json (100%) delete mode 100644 shipped.go diff --git a/AGENTS.md b/AGENTS.md index 0e3dace..b866da8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -88,7 +88,7 @@ In [docs/decisions.md](docs/decisions.md): - A cell may read a reference, and each row its own - A path is walked once without drawing before it is walked for real - A country's postal codes and streets are siblings under its locality -- A locale's `address` reads its country's `geo` tree, so a locale folder is no data set on its own +- A locale's `address` reads its country's `geo` tree, and the locale module's manifest reads that module - The default embed holds every Swedish postort the import can place and give a street-delivery code and a street, and the US places of 25,000 or more - A locale's `address` binds its country's record once and prints it whole - A postort's kommun comes from its name, its tätort or its codes, never from distance diff --git a/CHANGELOG.md b/CHANGELOG.md index a6dde29..cd2f818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,8 +125,15 @@ replacement, and each removed path, column or flag. its own quotes, a row of a table with no key is named as the path that selects it, `sv_SE.sex[f].first-name[Kim]`, and a category with no columns names the record that gives it one. +- The shipped data comes in Go packages, one per module, each exporting `FS`: + `github.com/larvit/fejkdata/data/en_US`, `…/sv_SE`, `…/misc`, `…/geo/SE` and + `…/geo/US`. `data.Modules()`, from `github.com/larvit/fejkdata/data`, lists every one, + and the CLI carries them all. `New` loads the modules `WithDataFS(modules...)` names and + the folders `WithDataPath` names, and fails, naming `WithDataFS`, where no such option + is given; `WithDataFS()` loads none. A manifest's `reads` lists the modules its source + reads by default, and a reference naming nothing names them, or says there are none. - A data source whose root holds a `.fejkdata.json` manifest with an `index` loads each - category on the first call reaching it, as the shipped set does. Data failing to load, + category on the first call reaching it, as each shipped module does. Data failing to load, in `New` or on that first call, gives an error matching `ErrLoad`, and the CLI exits 1. A source without an index loads in `New`, with the shipped categories it reads. Every other shipped category loads on the first call reaching it, so one your replacement diff --git a/README.md b/README.md index 3c1a334..047325e 100644 --- a/README.md +++ b/README.md @@ -160,12 +160,14 @@ it to a name and read the name ## Data -The shipped set under [`data/`](data) is embedded, so the CLI and the library -work with no data on disk. It holds one folder per locale (`en_US`, `sv_SE`), a -`geo` folder with a tree of places per country (`SE`, `US`), and a -locale-neutral `misc` folder. A directory is a namespace: each JSON file is a category named -after the file, each subdirectory a dot-path segment, so `mydata/sv_SE/person.json` -is `sv_SE.person` and replaces the shipped one. +The shipped data comes in modules, each a Go package under [`data/`](data) that embeds +its files, so nothing is read from disk: one per locale (`en_US`, `sv_SE`), one per +country's tree of places (`geo/SE`, `geo/US`), and the locale-neutral `misc`. The CLI +carries every module, and a library loads those it imports ([Library](#library)). A +directory is a namespace: each JSON file is a category named after the file, each +subdirectory a dot-path segment, so `mydata/sv_SE/person.json` is `sv_SE.person` and +replaces the shipped one. A module's tree spells its namespace the same way, so +`sv_SE`'s person is `data/sv_SE/sv_SE/person.json`. Sources merge in order; matching folders combine, any other clash is won by the last loaded. Names may not use `.`, `|`, `(`, `{`, `}`, `[`, `]`, `"`, `/` or ` as `, which binds a [name](#names), nor be `-`, which a struct tag reserves. A dot-prefixed @@ -181,13 +183,19 @@ every category of its source, so a category file it leaves out never loads. The index maps each category's dot path to an entry: `parent`, the table it links to, which needs its own entry in the same folder; and `paths`, every path `List` advertises -below the category, `""` for the category itself. The shipped set's -[`data/.fejkdata.json`](data/.fejkdata.json) is one, so delete it from a copy of `data/` -you edit, or keep its index current. A table whose parent column differs from its +below the category, `""` for the category itself. Each shipped module's manifest, such as +[`data/sv_SE/.fejkdata.json`](data/sv_SE/.fejkdata.json), is one, so delete it from a +copy you edit, or keep its index current. A table whose parent column differs from its entry's `parent` fails at first reach, and a table linking under another source's table needs a source without an index. `//go:embed` of a directory leaves the manifest out, and the source then loads whole in `New`; name it in the pattern too. +A manifest's `reads` lists, by any name its author chooses, every module the source +reads by default, directly or through another; a manifest may carry it without an +index. A shipped module names a Go package: `sv_SE`'s manifest reads +`github.com/larvit/fejkdata/data/geo/SE`. A reference naming nothing fails with the +modules its category's manifest reads, or says it names none. + Each locale carries `address`, `color`, `company`, `date`, `email`, `first-name`, `ip`, `last-name`, `person`, `phone`, `price`, `sentence`, `sex`, `time`, `url`, `username`, `version` and `word`, formatted per locale; `sv_SE` adds `personnummer` @@ -322,7 +330,12 @@ go get github.com/larvit/fejkdata # Go 1.22+ ``` ```go -f, err := fejkdata.New(fejkdata.WithSeed(42)) +import ( + "github.com/larvit/fejkdata" + "github.com/larvit/fejkdata/data" +) + +f, err := fejkdata.New(fejkdata.WithDataFS(data.Modules()...), fejkdata.WithSeed(42)) if err != nil { log.Fatal(err) } @@ -342,8 +355,13 @@ ok, err := fejkdata.IsTemplate(arg) // an inline template by its shape, el |--------|--| | `WithSeed(n)` | same seed, version and data: identical sequence | | `WithDataPath(dir)` | layer a directory; repeat to layer several, the last wins a clash | -| `WithDataFS(fsys)` | layer an `fs.FS`, such as your own `embed.FS` | -| `WithoutShippedData()` | load only what you give | +| `WithDataFS(modules...)` | layer modules in order, each an `fs.FS`: a shipped package's `FS`, or your own `embed.FS`; `WithDataFS()` loads none | + +`data.Modules()` is every shipped module, and links all of them into your program. To link +only the ones you read, pass each package's `FS` with the modules its manifest reads: +`WithDataFS(sv_SE.FS, SE.FS)` from `github.com/larvit/fejkdata/data/sv_SE` and +`github.com/larvit/fejkdata/data/geo/SE`. `New` fails where no `WithDataFS` or +`WithDataPath` names the data to load. `New` refuses data that breaks the grammar, could mean two things or cannot render a valid value, and `NewTemplate` refuses such a template. A category of an indexed source is @@ -762,7 +780,7 @@ A reference renders a node from elsewhere in the data — the path `Fake` takes, across every loaded source — so one category borrows another. The sigil says where the path starts, as in a filesystem: `{/en_US.person}` from the data root, `{.username}` from the folder this file sits in, `{..username}` from the folder -above. `data/sv_SE/email.json` can therefore read its own locale's `username` +above. `data/sv_SE/sv_SE/email.json` can therefore read its own locale's `username` without naming `sv_SE`: ```json @@ -1003,7 +1021,6 @@ Where the project is heading; the sections before Audience document what ships t ``` doc.go the package doc, and the vocabulary the package is written in fejkdata.go Generator, New, options, List -shipped.go the embedded data set index.go a source's index: its categories left unloaded, and loading one on the first call reaching it node.go the node model and JSON -> node compilation table.go tables: their options, format and cells, the link to the parent beside them, and how a path passes and draws a table @@ -1036,7 +1053,7 @@ internal/invariant/ the one phrase every package panics with when an invariant b internal/jsonvalue/ a decoded JSON value, its numbers kept as written, the one input a template compiles from, and the kind an error names it by internal/proven/ what a proof knows of a value, and the bounds a calc takes from its operands internal/rows/ a table's rows: the TSV, the options proved over it, the links between tables, row selection and draws, and the pin set one path or one named pick fixes -data/ shipped data (JSON, and a TSV per table), embedded at build: locale folders, geo, misc +data/ the shipped modules, a Go package each that embeds its categories (JSON, and a TSV per table) and its manifest, and Modules, which lists them data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md), and the modules they share docs/ the decision log, the register research behind the shipped data, the survey of other libraries, panels' scores and rulings on how readable the code is, and what each chunk cost an agent to read release-tooling/ the release CI publishes from the changelog heading @@ -1062,7 +1079,7 @@ Commands that rewrite source keep your file ownership when run with `--user`: ```sh docker compose run --rm --user "$(id -u):$(id -g)" fmt # gofmt -w . -docker compose run --rm --user "$(id -u):$(id -g)" generate # go generate: data/.fejkdata.json from data/ +docker compose run --rm --user "$(id -u):$(id -g)" generate # go generate: each data/ module's .fejkdata.json docker compose run --rm --user "$(id -u):$(id -g)" tidy # go mod tidy ``` @@ -1093,8 +1110,8 @@ Goal 3 is scored by a panel of simulated readers, and [`AGENTS.md`](AGENTS.md) s what the score asks of a pull request. Goal 5 is checked against simulated template writers from the Audience. -A change to the shipped data runs `generate` above first and commits -[`data/.fejkdata.json`](data/.fejkdata.json) with the change, then re-pins +A change to the shipped data runs `generate` above first and commits each module's +`.fejkdata.json` with the change, then re-pins [`testdata/shipped_shape.txt`](testdata/shipped_shape.txt) in its own commit: ```sh diff --git a/cmd/fejkdata/main.go b/cmd/fejkdata/main.go index 056faae..ea2f010 100644 --- a/cmd/fejkdata/main.go +++ b/cmd/fejkdata/main.go @@ -13,6 +13,7 @@ import ( "errors" "fmt" "io" + "io/fs" "os" "runtime/debug" "sort" @@ -20,6 +21,7 @@ import ( "strings" "github.com/larvit/fejkdata" + "github.com/larvit/fejkdata/data" ) const usage = `Usage: