Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,817 changes: 3,685 additions & 2,132 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions packages/cli/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const newCommand = require("./new");
const getDevCommand = require("./dev");
const getBuildCommand = require("./build");
const getServeCommand = require("./serve");
const getRenderCommand = require("./render");

const buildLocalCommands = (cli, isMechanicProject) => {
const directory = path.resolve(".");
Expand All @@ -18,12 +19,12 @@ const buildLocalCommands = (cli, isMechanicProject) => {
if (!isMechanicProject) {
console.log(
fail("Not mechanic project: ") +
`mechanic ${command} can only be run inside mechanic project.`
`mechanic ${command} can only be run inside mechanic project.`
);
console.log(`Current directory: ${directory}`);
console.log(
`Either the current working directory does not contain a valid package.json or ` +
`'${mechanic}' is not specified as a dependency \n`
`'${mechanic}' is not specified as a dependency \n`
);

cli.showHelp();
Expand Down Expand Up @@ -73,7 +74,7 @@ const buildLocalCommands = (cli, isMechanicProject) => {

const getCommandHandler = (commandName, handler) => {
return argv => {
const localCmd = resolveLocalCommand(commandName) || (() => {});
const localCmd = resolveLocalCommand(commandName) || (() => { });
const args = { ...argv, directory };
return handler ? handler(args, localCmd) : localCmd(args);
};
Expand All @@ -82,7 +83,8 @@ const buildLocalCommands = (cli, isMechanicProject) => {
cli
.command(getDevCommand(getCommandHandler))
.command(getBuildCommand(getCommandHandler))
.command(getServeCommand(getCommandHandler));
.command(getServeCommand(getCommandHandler))
.command(getRenderCommand(getCommandHandler));
};

module.exports = () => {
Expand Down
27 changes: 27 additions & 0 deletions packages/cli/commands/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const path = require("path");
module.exports = getCommandHandler => ({
command: "render functionName outputFile [configPath] [distDir]",
aliases: ["r"],
desc: "Headlessly renders a design function",
builder: yargs =>
yargs
.options({
functionName: {
description: "Name of the function to render"
},
outputFile: {
description: "Path to the file to write to"
},
configPath: {
type: "string",
description: "Path to mechanic config file"
},
distDir: {
type: "string",
description: "Custom build directory"
},
})
.default("configPath", path.normalize("./mechanic.config.js"))
.default("distDir", path.normalize("./dist")),
handler: getCommandHandler("render")
});
13 changes: 13 additions & 0 deletions packages/core/app/components/SideBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useSeedHistory } from "./utils/useSeedHistory.js";
import { Button, Toggle } from "@mechanic-design/ui-components";
import { Input } from "./Input.js";
import { useInteractiveInputs } from "./utils/useInteractiveInputs.js";
import { download } from "./utils/download.js";

import { appComponents } from "../APP";

Expand Down Expand Up @@ -111,6 +112,10 @@ export const SideBar = ({
lastRun.downloadState(name);
};

const handleFileDownload = ({ data, name, mimeType }) => {
download(data, name, mimeType);
}

useEffect(() => {
if (autoRefreshOn && iframeLoaded) preview();
}, [values, autoRefreshOn, iframeLoaded, scaleToFit]);
Expand All @@ -119,6 +124,14 @@ export const SideBar = ({
preview();
}, [seedHistory.current]);

useEffect(() => {
if (lastRun === null) return;

lastRun.on("download", handleFileDownload);

return () => lastRun.off("download", handleFileDownload);
}, [lastRun]);

useInteractiveInputs(inputs, iframe, handleOnChange);
useShortcuts(handleExport);

Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@babel/preset-react": "^7.14.5",
"@babel/runtime": "^7.14.6",
"@mechanic-design/fonts": "^1.1.0",
"@mechanic-design/headless": "^2.0.0-beta.10",
"@mechanic-design/ui-components": "^2.0.0-beta.10",
"@mechanic-design/utils": "^2.0.0-beta.10",
"babel-loader": "^8.2.2",
Expand Down
96 changes: 96 additions & 0 deletions packages/core/src/commands/render.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const path = require("path");
const fs = require("fs");
const headless = require("@mechanic-design/headless");

const { formatTimestamp, getConfig, writeToFile } = require("./utils.cjs");

const {
spinners: { mechanicSpinner: spinner },
colors: { success }
} = require("@mechanic-design/utils");

/**
* Removes all expected parameters from the argv object.
* Returns remaining parameters, so they can be passed as
* inputs to the design function.
* @param {object} argv
* @return object
*/
const getParameters = argv => {
const IGNORE_LIST = [
"_",
"configPath",
"config-path",
"distDir",
"dist-dir",
"$0",
"functionName",
"function-name",
"outputFile",
"output-file",
"directory"
];

let obj = {};

for (const key of Object.keys(argv)) {
if (!IGNORE_LIST.includes(key)) obj[key] = argv[key];
}

return obj;
};

const command = async argv => {
const { functionName } = argv;
spinner.start("Setting up");

const { config, configPath } = await getConfig(argv.configPath);

if (!config) {
spinner.fail(`Mechanic config file (${configPath}) not found`);
return;
}

const distDir = path.normalize(
config.distDir != null
? config.distDir
: argv.distDir != null
? argv.distDir
: "./dist"
);

if (fs.existsSync(distDir)) {
spinner.succeed();
} else {
spinner.fail(
"No build directory found. Make sure to run mechanic build first."
);
return;
}

spinner.start("Rendering");

await headless.render({
distDir,
functionName,
parameters: getParameters(argv),
done: ({ data, duration }) => {
try {
const outPath = path.join(process.cwd(), argv.outputFile);
writeToFile(outPath, data);
spinner.succeed(`Rendered`);
console.log();
console.log(`Output\t${success(outPath)}`);
console.log(`Took\t${success(formatTimestamp(duration))}`);
} catch (e) {
spinner.fail(`Error rendering.`);
console.log(e);
}
},
hooks: {
frame: (count) => spinner.text = `Rendering frame ${count}`,
}
});
};

module.exports = command;
16 changes: 13 additions & 3 deletions packages/core/src/commands/utils.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ const inputsPath = path.resolve(
const getFuncScriptContent = designFunctionPath => `
import { inputsDefs, inputErrors } from "${inputsPath}";
import * as designFunction from "${designFunctionPath
.split(path.sep)
.join("/")}";
.split(path.sep)
.join("/")}";
import { setUp } from "${setUpFunctionPath.split(path.sep).join("/")}";
setUp(inputsDefs, designFunction, inputErrors)
if (module.hot) {
Expand Down Expand Up @@ -96,6 +96,7 @@ const generateFuncTempScripts = functionsPath => {
);
designFunctions[name]["temp"] = tempScriptName;
});

return [designFunctions, tempDirObj];
};

Expand Down Expand Up @@ -133,14 +134,21 @@ const generateInputScript = inputsPath => {
};

const setCustomInterrupt = (callback, tempDirObjs = []) => {
process.on("SIGINT", function () {
process.on("SIGINT", function() {
if (tempDirObjs.length > 0)
tempDirObjs.forEach(obj => obj.removeCallback());
callback();
process.exit();
});
};

const writeToFile = (file, data) => {
fs.writeFileSync(file, data);
};

const formatTimestamp = ms =>
`${Math.round((ms / 1000) * 100 + Number.EPSILON) / 100}s`;

const greet = () => {
console.log(`${mechanic}
`);
Expand All @@ -162,6 +170,8 @@ module.exports = {
generateInputScript,
generateFuncTempScripts,
setCustomInterrupt,
writeToFile,
formatTimestamp,
greet,
goodbye
};
87 changes: 77 additions & 10 deletions packages/core/src/mechanic.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import seedrandom from "seedrandom";
import { download } from "./download.js";
import { WebMWriter } from "./webm-writer.js";
import {
isSVG,
Expand Down Expand Up @@ -80,6 +79,56 @@ export class Mechanic {
this.values = values;
this.functionState = lastRun?.functionState ?? {};
this.exportType = exportType;
this.events = {};
}

/**
* Registers an event listener with the runtime.
* @param {string} event
* @param {function} listener
*/
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}

this.events[event].push(listener);
}

/**
* Unsubscribes an event listener from the runtime.
* @param {string} event
* @param {function} listener
*/
off(event, listener) {
let idx;

if (typeof this.events[event] === "object") {
idx = this.events[event].indexOf(listener);

if (idx > -1) {
this.events[event].splice(idx, 1);
}
}
}

/**
* Emits an event to all subscribed listeners.
* @param {string} event
* @perem {Array<any>} args
* @private
*/
emit(event, ...args) {
let i, listeners, length;

if (typeof this.events[event] === "object") {
listeners = this.events[event].slice();
length = listeners.length;

for (i = 0; i < length; i++) {
listeners[i].apply(this, args);
}
}
}

/**
Expand Down Expand Up @@ -151,6 +200,7 @@ export class Mechanic {
this.videoWriter.addFrame(frame);
}
this.frameCalled = true;
this.emit("afterHandleFrame");
}

/**
Expand Down Expand Up @@ -224,6 +274,7 @@ export class Mechanic {
this.videoData = await this.videoWriter.complete();
}
this.isDone = true;
this.emit("afterHandleDone");
}

/**
Expand All @@ -240,13 +291,29 @@ export class Mechanic {
throw "The download function can only be called after the done() function has finished";
}
if (this.svgData) {
download(this.svgData, `${name}.svg`, "image/svg+xml");
this.emit("download", {
data: this.svgData,
name: `${name}.svg`,
mimeType: "image/svg+xml"
});
} else if (this.canvasData) {
download(this.canvasData, `${name}.png`, "image/png");
this.emit("download", {
data: this.canvasData,
name: `${name}.png`,
mimeType: "image/png"
});
} else if (this.htmlData) {
download(this.htmlData, `${name}.png`, "image/png");
this.emit("download", {
data: this.htmlData,
name: `${name}.png`,
mimeType: "image/png"
});
} else if (this.videoData) {
download(this.videoData, `${name}.webm`, "video/webm");
this.emit("download", {
data: this.videoData,
name: `${name}.webm`,
mimeType: "video/webm"
});
}
}

Expand All @@ -262,10 +329,10 @@ export class Mechanic {
if (!this.functionState) {
throw "The downloadState if a state has been set.";
}
download(
JSON.stringify(this.functionState),
`${name}.json`,
"application/json"
);
this.emit("download", {
data: JSON.stringify(this.functionState),
name: `${name}.json`,
mimeType: "application/json"
});
}
}
3 changes: 2 additions & 1 deletion packages/dsi-logo-maker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"scripts": {
"dev": "mechanic dev",
"build": "mechanic build",
"serve": "mechanic serve"
"serve": "mechanic serve",
"render": "mechanic render"
},
"dependencies": {
"@babel/runtime": "^7.14.6",
Expand Down
Empty file added packages/headless/.gitingore
Empty file.
Loading