create-rulecad 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JoniBach and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,11 @@
1
- # Temporary Holding Version
1
+ # create-rulecad
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ```sh
4
+ npm create rulecad my-designs
5
+ cd my-designs
6
+ npx cad check home/soap-tray
7
+ ```
8
+
9
+ Makes a [rulecad](https://github.com/JoniBach/rulecad) workspace with one design to start from: a soap tray with
10
+ ribs and drain slots, every measurement a variable that names its rule. `--no-install` makes the files without
11
+ running `npm install`.
package/index.mjs ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // npm create rulecad [folder]: a new rulecad workspace with a design to start from (a soap tray), then npm install.
3
+ // --no-install make the files only
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { execSync } from 'node:child_process';
7
+
8
+ const args = process.argv.slice(2), install = !args.includes('--no-install');
9
+ const dir = path.resolve(args.find((a) => !a.startsWith('--')) || 'my-designs'), name = path.basename(dir);
10
+ if (fs.existsSync(dir) && fs.readdirSync(dir).length) { console.error(`${dir} isn't empty: name a new folder (npm create rulecad my-designs)`); process.exit(1); }
11
+
12
+ const TEMPLATE = new URL('./template/', import.meta.url).pathname;
13
+ fs.cpSync(TEMPLATE, dir, { recursive: true });
14
+ fs.renameSync(path.join(dir, 'gitignore'), path.join(dir, '.gitignore')); // npm drops files named .gitignore from packages
15
+ const readme = path.join(dir, 'README.md');
16
+ fs.writeFileSync(readme, fs.readFileSync(readme, 'utf8').replaceAll('NAME', name));
17
+ // $RULECAD_LOCAL: a rulecad checkout to use instead of npm (to try unpublished changes)
18
+ const from = (pkg) => (process.env.RULECAD_LOCAL ? `file:${path.join(path.resolve(process.env.RULECAD_LOCAL), 'packages', pkg)}` : '^0.1.0');
19
+ fs.writeFileSync(path.join(dir, 'package.json'), `${JSON.stringify({
20
+ name, private: true, type: 'module',
21
+ scripts: { check: 'cad check home/soap-tray' },
22
+ dependencies: { '@rulecad/cad': from('cad'), '@jscad/modeling': '^2.12.0' },
23
+ }, null, 2)}\n`);
24
+
25
+ console.log(`made ${path.relative(process.cwd(), dir) || '.'}: a soap tray to start from, in projects/home`);
26
+ if (install) execSync('npm install', { cwd: dir, stdio: 'inherit' });
27
+ console.log(`\n cd ${path.relative(process.cwd(), dir)}\n npx cad check home/soap-tray\n npx cad preview home/soap-tray\n\nREADME.md says what's where.`);
package/package.json CHANGED
@@ -1,6 +1,31 @@
1
1
  {
2
2
  "name": "create-rulecad",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Start a rulecad project: npm create rulecad my-designs. A workspace with a soap tray to start from, ready to check, preview, export and share.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-rulecad": "index.mjs"
8
+ },
9
+ "files": [
10
+ "index.mjs",
11
+ "template",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "license": "MIT",
16
+ "keywords": [
17
+ "rulecad",
18
+ "create",
19
+ "3d-printing",
20
+ "jscad",
21
+ "scaffold"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20.6"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/JoniBach/rulecad.git",
29
+ "directory": "packages/create-rulecad"
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ # NAME
2
+
3
+ Designs for 3D printing, made with [rulecad](https://github.com/JoniBach/rulecad). Every measurement is a variable,
4
+ and every variable says where it comes from.
5
+
6
+ ```sh
7
+ npx cad check home/soap-tray # is it printable? (and: no raw numbers in the design)
8
+ npx cad check home/soap-tray --set length=140 # try another size (each tweakable variable has a range)
9
+ npx cad preview home/soap-tray # six views, in .preview/
10
+ npx cad export home/soap-tray # a 3MF for your slicer, in exports/
11
+ npx cad link home/soap-tray --md # a link that opens the model in a browser: nothing is uploaded
12
+ npx cad new home/hook # your own design
13
+ ```
14
+
15
+ - `projects/home/objects/soap-tray.json` holds the variables, `soap-tray.js` the shape ([JSCAD](https://openjscad.xyz)).
16
+ - `rules/` holds the facts designs use, such as your printer's (`x1c.json`, copied in on first use). Measure your own
17
+ printer and correct them: every design that uses them follows.
18
+ - `cad.config.json` names the printer. For another printer, copy `rules/x1c.json` under a new name and change the
19
+ values.
@@ -0,0 +1,4 @@
1
+ {
2
+ "printer": "x1c",
3
+ "targets": {}
4
+ }
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ exports/
3
+ .preview/
4
+ .versions/
5
+ .cache/
6
+ .DS_Store
@@ -0,0 +1,19 @@
1
+ // A soap tray: a shallow box with ribs across the floor to hold the soap up, and drain slots between them.
2
+ // Every measurement is a variable in soap-tray.json; this file only says how they combine. Z is up, the bed is z = 0.
3
+ import jscad from '@jscad/modeling'
4
+ const { cuboid } = jscad.primitives
5
+ const { translate } = jscad.transforms
6
+ const { subtract, union } = jscad.booleans
7
+
8
+ // A box sitting on z = z0, centred in x and y
9
+ const box = (x, y, z0, h, cx = 0) => translate([cx, 0, z0 + h / 2], cuboid({ size: [x, y, h] }))
10
+
11
+ export function main (v) {
12
+ const inside = [v.length - v.wall * 2, v.width - v.wall * 2]
13
+ const count = Math.floor(inside[0] / v.pitch) // ribs, centred along the length
14
+ const at = (i) => (i - (count - 1) / 2) * v.pitch
15
+ const ribs = Array.from({ length: count }, (_, i) => box(v.rib_width, inside[1], v.floor / 2, v.rib_height + v.floor / 2, at(i)))
16
+ const slots = Array.from({ length: count - 1 }, (_, i) => box(v.slot, inside[1] - v.margin * 2, -v.floor / 2, v.floor * 2, (at(i) + at(i + 1)) / 2))
17
+ const tray = subtract(box(v.length, v.width, 0, v.height), box(inside[0], inside[1], v.floor, v.height))
18
+ return subtract(union(tray, ...ribs), ...slots)
19
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "soap-tray",
3
+ "title": "Soap tray",
4
+ "version": "0.1.0",
5
+ "uses": ["x1c"],
6
+ "material": "PLA",
7
+ "orientation": "Flat side down on the bed. Walls are straight up, so it prints without supports.",
8
+ "variables": {
9
+ "length": { "value": 110, "unit": "mm", "rule": "Long enough for a bar of soap", "label": "Length", "show": true, "tweak": { "min": 70, "max": 180 } },
10
+ "width": { "value": 75, "unit": "mm", "rule": "Wide enough for a bar of soap", "label": "Width", "show": true, "tweak": { "min": 50, "max": 120 } },
11
+ "height": { "value": "x1c.layer * 60", "unit": "mm", "rule": "A whole number of layers", "label": "Height", "show": true },
12
+ "wall": { "value": "x1c.line * 4", "unit": "mm", "rule": "Four print lines: stiff, and no gaps between lines", "label": "Wall" },
13
+ "floor": { "value": "x1c.layer * 8", "unit": "mm", "rule": "Eight layers under the ribs" },
14
+ "rib_height": { "value": "x1c.layer * 15", "unit": "mm", "rule": "Holds the soap up out of the water", "label": "Rib height", "show": true },
15
+ "rib_width": { "value": "x1c.line * 4", "unit": "mm", "rule": "Four print lines" },
16
+ "pitch": { "value": 12, "unit": "mm", "rule": "Ribs this far apart: soap rests on several, water runs between", "label": "Rib spacing", "show": true, "tweak": { "min": 8, "max": 20 } },
17
+ "slot": { "value": "3 + x1c.hole_comp", "unit": "mm", "rule": "Drain slots between the ribs, widened for how holes print small" },
18
+ "margin": { "value": 6, "unit": "mm", "rule": "Solid floor between the slots and the walls" }
19
+ }
20
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "home",
3
+ "description": "Things for the house.",
4
+ "material": "PLA",
5
+ "objects": ["soap-tray"]
6
+ }