@interactivethings/scripts 2.1.4 → 2.2.1

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.
Files changed (37) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cloudflare/cli.d.ts +13 -0
  3. package/dist/cloudflare/cloudflare.d.ts +69 -0
  4. package/dist/cloudflare/deployments.d.ts +9 -0
  5. package/dist/config.d.ts +92 -0
  6. package/dist/config.js +0 -11
  7. package/dist/figma/api.d.ts +69 -0
  8. package/dist/figma/cli.d.ts +30 -0
  9. package/dist/figma/cli.test.d.ts +1 -0
  10. package/dist/figma/images.d.ts +1 -0
  11. package/dist/figma/utils.d.ts +10 -0
  12. package/dist/index.d.ts +8 -0
  13. package/dist/init/cli.d.ts +5 -0
  14. package/dist/shared/cli.d.ts +32 -0
  15. package/dist/shared/deployment-service.d.ts +7 -0
  16. package/dist/shared/wait-deployment.d.ts +7 -0
  17. package/dist/tokens-studio/__tests__/fixtures/invalid-transformer.d.ts +9 -0
  18. package/dist/tokens-studio/__tests__/fixtures/test-transformer.d.ts +18 -0
  19. package/dist/tokens-studio/cli.d.ts +14 -0
  20. package/dist/tokens-studio/cli.test.d.ts +1 -0
  21. package/dist/tokens-studio/index.d.ts +9 -0
  22. package/dist/tokens-studio/index.js +6 -1
  23. package/dist/tokens-studio/require.d.ts +3 -0
  24. package/dist/tokens-studio/types.d.ts +22 -0
  25. package/dist/tokens-studio/utils.d.ts +60 -0
  26. package/dist/tokens-studio/utils.js +37 -1
  27. package/dist/types.d.ts +2 -0
  28. package/dist/vercel/cli.d.ts +11 -0
  29. package/dist/vercel/cli.test.d.ts +1 -0
  30. package/dist/vercel/deployments.d.ts +8 -0
  31. package/dist/vercel/vercel.d.ts +15 -0
  32. package/examples/README.md +62 -0
  33. package/examples/custom-transform.js +41 -0
  34. package/examples/ixt.config.example.ts +33 -0
  35. package/examples/mui-transform.ts +158 -0
  36. package/examples/tailwind-transform.ts +167 -0
  37. package/package.json +3 -2
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(): Promise<void>;
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ type Args = {
3
+ subcommand: "wait-deployment";
4
+ commit: string;
5
+ interval: number;
6
+ timeout: number;
7
+ project?: string;
8
+ email?: string;
9
+ account_id?: string;
10
+ config?: string;
11
+ };
12
+ declare const _default: import("../shared/cli").CLIModule<Args>;
13
+ export default _default;
@@ -0,0 +1,69 @@
1
+ import { IDeploymentService, Deployment } from "../shared/deployment-service";
2
+ /** /!\ Inferred with ChatGPT */
3
+ export type CloudflareDeployment = {
4
+ id: string;
5
+ short_id: string;
6
+ project_id: string;
7
+ project_name: string;
8
+ environment: string;
9
+ url: string;
10
+ created_on: string;
11
+ modified_on: string;
12
+ latest_stage: {
13
+ name: string;
14
+ started_on: string;
15
+ ended_on: string;
16
+ status: string;
17
+ };
18
+ deployment_trigger: {
19
+ type: string;
20
+ metadata: {
21
+ branch: string;
22
+ commit_hash: string;
23
+ commit_message: string;
24
+ commit_dirty: boolean;
25
+ };
26
+ };
27
+ stages: {
28
+ name: string;
29
+ started_on: string;
30
+ ended_on: string;
31
+ status: string;
32
+ }[];
33
+ build_config: {
34
+ build_command: string;
35
+ destination_dir: string;
36
+ build_caching: boolean;
37
+ root_dir: string;
38
+ web_analytics_tag: string | null;
39
+ web_analytics_token: string | null;
40
+ };
41
+ source: {
42
+ type: string;
43
+ config: {
44
+ owner: string;
45
+ repo_name: string;
46
+ production_branch: string;
47
+ pr_comments_enabled: boolean;
48
+ };
49
+ };
50
+ env_vars: Record<string, {
51
+ type: "secret_text" | "plain_text";
52
+ value: string;
53
+ }>;
54
+ compatibility_date: string;
55
+ compatibility_flags: string[];
56
+ build_image_major_version: number;
57
+ usage_model: string;
58
+ aliases: string[];
59
+ is_skipped: boolean;
60
+ production_branch: string;
61
+ };
62
+ export declare class CloudflareDeploymentService implements IDeploymentService {
63
+ private accountId;
64
+ private projectName;
65
+ private email;
66
+ private apiKey;
67
+ constructor(accountId: string, projectName: string, email: string, apiKey: string);
68
+ fetchForCommit(commitSha: string): Promise<Deployment[]>;
69
+ }
@@ -0,0 +1,9 @@
1
+ export declare function waitForDeploymentReady({ accountId, project, email, apiKey, commitSha, interval, timeout, }: {
2
+ accountId: string;
3
+ project: string;
4
+ email: string;
5
+ apiKey: string;
6
+ commitSha: string;
7
+ interval: number;
8
+ timeout: number;
9
+ }): Promise<import("../shared/deployment-service").Deployment | undefined>;
@@ -0,0 +1,92 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Figma configuration schema
4
+ */
5
+ export declare const FigmaAssetConfigSchema: z.ZodObject<{
6
+ url: z.ZodString;
7
+ output: z.ZodString;
8
+ name: z.ZodString;
9
+ }, z.core.$strip>;
10
+ export declare const FigmaConfigSchema: z.ZodObject<{
11
+ token: z.ZodOptional<z.ZodString>;
12
+ assets: z.ZodDefault<z.ZodArray<z.ZodObject<{
13
+ url: z.ZodString;
14
+ output: z.ZodString;
15
+ name: z.ZodString;
16
+ }, z.core.$strip>>>;
17
+ }, z.core.$strip>;
18
+ /**
19
+ * Tokens Studio configuration schema
20
+ */
21
+ export declare const TokensStudioConfigSchema: z.ZodObject<{
22
+ input: z.ZodDefault<z.ZodString>;
23
+ output: z.ZodOptional<z.ZodString>;
24
+ handler: z.ZodString;
25
+ }, z.core.$strip>;
26
+ /**
27
+ * Vercel configuration schema
28
+ */
29
+ export declare const VercelConfigSchema: z.ZodObject<{
30
+ team: z.ZodOptional<z.ZodString>;
31
+ project: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strip>;
33
+ /**
34
+ * Cloudflare configuration schema
35
+ */
36
+ export declare const CloudflareConfigSchema: z.ZodObject<{
37
+ project: z.ZodOptional<z.ZodString>;
38
+ email: z.ZodOptional<z.ZodString>;
39
+ accountId: z.ZodOptional<z.ZodString>;
40
+ }, z.core.$strip>;
41
+ /**
42
+ * Main IXT Scripts configuration schema
43
+ */
44
+ export declare const IxtConfigSchema: z.ZodObject<{
45
+ figma: z.ZodOptional<z.ZodObject<{
46
+ token: z.ZodOptional<z.ZodString>;
47
+ assets: z.ZodDefault<z.ZodArray<z.ZodObject<{
48
+ url: z.ZodString;
49
+ output: z.ZodString;
50
+ name: z.ZodString;
51
+ }, z.core.$strip>>>;
52
+ }, z.core.$strip>>;
53
+ tokensStudio: z.ZodOptional<z.ZodObject<{
54
+ input: z.ZodDefault<z.ZodString>;
55
+ output: z.ZodOptional<z.ZodString>;
56
+ handler: z.ZodString;
57
+ }, z.core.$strip>>;
58
+ vercel: z.ZodOptional<z.ZodObject<{
59
+ team: z.ZodOptional<z.ZodString>;
60
+ project: z.ZodOptional<z.ZodString>;
61
+ }, z.core.$strip>>;
62
+ cloudflare: z.ZodOptional<z.ZodObject<{
63
+ project: z.ZodOptional<z.ZodString>;
64
+ email: z.ZodOptional<z.ZodString>;
65
+ accountId: z.ZodOptional<z.ZodString>;
66
+ }, z.core.$strip>>;
67
+ }, z.core.$strip>;
68
+ export type IxtConfig = z.infer<typeof IxtConfigSchema>;
69
+ export type FigmaConfig = z.infer<typeof FigmaConfigSchema>;
70
+ export type FigmaAssetConfig = z.infer<typeof FigmaAssetConfigSchema>;
71
+ export type TokensStudioConfig = z.infer<typeof TokensStudioConfigSchema>;
72
+ export type VercelConfig = z.infer<typeof VercelConfigSchema>;
73
+ export type CloudflareConfig = z.infer<typeof CloudflareConfigSchema>;
74
+ /**
75
+ * Helper function for type-safe configuration definition
76
+ * Provides IntelliSense and validation for the configuration object
77
+ */
78
+ export declare function defineConfig(config: IxtConfig): IxtConfig;
79
+ /**
80
+ * Load configuration from a TypeScript file
81
+ * Only supports .ts configuration files for better type safety
82
+ */
83
+ export declare function loadConfig(configPath?: string): Promise<IxtConfig>;
84
+ /**
85
+ * Merge CLI arguments with configuration
86
+ * CLI arguments take precedence over config file values
87
+ */
88
+ export declare function mergeConfigWithArgs<T extends Record<string, any>>(config: IxtConfig, args: T, section: keyof IxtConfig): T & Partial<IxtConfig[keyof IxtConfig]>;
89
+ /**
90
+ * Validate that required values are present after merging config and args
91
+ */
92
+ export declare function validateRequiredConfig<T extends Record<string, any>>(mergedConfig: T, requiredFields: (keyof T)[]): void;
package/dist/config.js CHANGED
@@ -107,17 +107,6 @@ exports.IxtConfigSchema = zod_1.z.object({
107
107
  function defineConfig(config) {
108
108
  return exports.IxtConfigSchema.parse(config);
109
109
  }
110
- /**
111
- * Default configuration values
112
- */
113
- const DEFAULT_CONFIG = {
114
- figma: {
115
- assets: [],
116
- },
117
- tokensStudio: {
118
- input: "./tokens",
119
- },
120
- };
121
110
  /**
122
111
  * Load configuration from a TypeScript file
123
112
  * Only supports .ts configuration files for better type safety
@@ -0,0 +1,69 @@
1
+ type Color = {
2
+ r: number;
3
+ g: number;
4
+ b: number;
5
+ a: number;
6
+ };
7
+ type FileId = string;
8
+ type NodeId = string;
9
+ export type FigmaNode = ({
10
+ type: "RECTANGLE";
11
+ fills: {
12
+ type: "SOLID";
13
+ color: Color;
14
+ }[];
15
+ effects: {
16
+ type: "DROP_SHADOW";
17
+ offset: {
18
+ x: number;
19
+ y: number;
20
+ };
21
+ color: Color;
22
+ radius: number;
23
+ }[];
24
+ } | {
25
+ type: "TEXT";
26
+ style: {
27
+ fontFamily: string;
28
+ fontWeight: number;
29
+ fontSize: number;
30
+ letterSpacing: number;
31
+ lineHeightPx: number;
32
+ };
33
+ }) & {
34
+ name: string;
35
+ id: string;
36
+ };
37
+ export declare const createAPI: (figmaToken: string) => {
38
+ file: {
39
+ fetch: (fileId: FileId) => Promise<any>;
40
+ };
41
+ nodes: {
42
+ fetch: (fileId: FileId, ids: NodeId[]) => Promise<any>;
43
+ };
44
+ styles: {
45
+ fetch: (fileId: FileId) => Promise<{
46
+ nodes: {
47
+ document: FigmaNode;
48
+ }[];
49
+ }>;
50
+ };
51
+ /**
52
+ * Given a root node, fetches all exports of the nodes in the tree.
53
+ * This is especially useful to fetch all icons or assets from a design system.
54
+ */
55
+ images: {
56
+ fetch: (fileId: FileId, nodeIds: string[], { onFetchSources, onFetchImage, }?: {
57
+ onFetchSources?: (sources: {
58
+ name: string;
59
+ format: string;
60
+ }[]) => void;
61
+ onFetchImage?: (url: string) => void;
62
+ }) => Promise<{
63
+ data: string | Buffer<ArrayBufferLike>;
64
+ name: string;
65
+ format: string;
66
+ }[]>;
67
+ };
68
+ };
69
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * This script downloads the assets from Figma and saves them into the repository.
3
+ * It is configured via the unified ixt configuration system.
4
+ *
5
+ * @example ixt.config.js
6
+ * import { defineConfig } from '@interactivethings/scripts';
7
+ *
8
+ * export default defineConfig({
9
+ * figma: {
10
+ * assets: [{
11
+ * name: "illustrations",
12
+ * url: "https://www.figma.com/design/ElWWZIcOGFhiT06rzfIwRO/Design-System?node-id=11861-10071",
13
+ * output: "src/assets/illustrations"
14
+ * }]
15
+ * }
16
+ * });
17
+ */
18
+ type FigmaArgs = {
19
+ config?: string;
20
+ token?: string;
21
+ name?: string;
22
+ subcommand: "download";
23
+ } | {
24
+ config?: string;
25
+ token?: string;
26
+ subcommand: "get";
27
+ url: string;
28
+ };
29
+ declare const _default: import("../shared/cli").CLIModule<FigmaArgs>;
30
+ export default _default;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const optimizeImage: (filepath: string) => Promise<void>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Should parse a URL in the form https://www.figma.com/design/ElWWZIcOGFhiT06rzfIwRO/Design-System-%5BTPW-Mobile-App%5D?node-id=10404-19140&p=f&t=K5K37NzOSt1uwjsX-0
3
+ * and extracts the pageId and nodeId
4
+ * The page id is the part just after /design
5
+ */
6
+ export declare const parseFigmaURL: (urlString: string) => {
7
+ figmaFileId: string;
8
+ figmaPageId: string;
9
+ };
10
+ export declare const formatFigmaURL: (figmaFileId: string, figmaPageId: string) => string;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @interactivethings/scripts
3
+ *
4
+ * A collection of useful development tools by Interactive Things
5
+ */
6
+ export * from "./types";
7
+ export { defineConfig, type IxtConfig, type FigmaConfig, type TokensStudioConfig, } from "./config";
8
+ export * as tokensStudio from "./tokens-studio";
@@ -0,0 +1,5 @@
1
+ type InitArgs = {
2
+ force?: boolean;
3
+ };
4
+ declare const _default: import("../shared/cli").CLIModule<InitArgs>;
5
+ export default _default;
@@ -0,0 +1,32 @@
1
+ import { ArgumentParser } from "argparse";
2
+ /**
3
+ * Base interface that all CLI modules must implement
4
+ */
5
+ export interface CLIModule<TArgs = any> {
6
+ /**
7
+ * Function to configure the argument parser for this module
8
+ */
9
+ configParser: (parser: ArgumentParser) => void;
10
+ /**
11
+ * Function to execute the module logic with parsed arguments
12
+ */
13
+ run: (args: TArgs) => Promise<void>;
14
+ }
15
+ /**
16
+ * Configuration object for defining a CLI module
17
+ */
18
+ export interface CLIModuleConfig<TArgs = any> {
19
+ /**
20
+ * Function to configure the argument parser
21
+ */
22
+ configParser: (parser: ArgumentParser) => void;
23
+ /**
24
+ * Function to execute the module logic
25
+ */
26
+ run: (args: TArgs) => Promise<void>;
27
+ }
28
+ /**
29
+ * Type-safe factory function for creating CLI modules
30
+ * Provides consistent interface and utilities for all CLI modules
31
+ */
32
+ export declare function defineCLIModule<TArgs = any>(config: CLIModuleConfig<TArgs>): CLIModule<TArgs>;
@@ -0,0 +1,7 @@
1
+ export type Deployment = {
2
+ state: string;
3
+ url: string;
4
+ };
5
+ export interface IDeploymentService {
6
+ fetchForCommit: (commitSha: string) => Promise<Deployment[]>;
7
+ }
@@ -0,0 +1,7 @@
1
+ import { IDeploymentService } from "./deployment-service";
2
+ export declare function waitForDeploymentReady({ commitSha, interval, timeout, deploymentService, }: {
3
+ commitSha: string;
4
+ interval: number;
5
+ timeout: number;
6
+ deploymentService: IDeploymentService;
7
+ }): Promise<import("./deployment-service").Deployment | undefined>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Invalid transformer for testing error cases
3
+ * This transformer does not export a transform function
4
+ */
5
+ declare function someOtherFunction(): string;
6
+ declare const _default: {
7
+ someOtherFunction: typeof someOtherFunction;
8
+ };
9
+ export default _default;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Test transformer for tokens-studio CLI tests
3
+ * This transformer simply wraps the input tokens with metadata
4
+ */
5
+ declare function transform(input: {
6
+ metadata: any;
7
+ tokenData: any;
8
+ }): {
9
+ source: string;
10
+ metadata: any;
11
+ tokens: any;
12
+ processed: boolean;
13
+ transformedAt: string;
14
+ };
15
+ declare const _default: {
16
+ transform: typeof transform;
17
+ };
18
+ export default _default;
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for tokens-studio transformations
4
+ * Supports different transformer handlers via --handler argument
5
+ */
6
+ type TokenStudioArgs = {
7
+ config?: string;
8
+ subcommand: "transform";
9
+ handler?: string;
10
+ input?: string;
11
+ output?: string;
12
+ };
13
+ declare const _default: import("../shared/cli").CLIModule<TokenStudioArgs>;
14
+ export default _default;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tokens Studio module for @interactivethings/scripts
3
+ *
4
+ * This module provides utilities and transformers for working with
5
+ * tokens exported from Tokens Studio (Figma plugin).
6
+ */
7
+ export { simplifyValues, kebabCase, mapEntries, maybeParseToNumber, maybeParseToPx, isToken, deepMerge, resolveReferences, formatValues, flattenTokens, makeResolver, mapKeysRecursively, defineTransform, } from "./utils";
8
+ export type { Metadata, TransformInput, TransformerModule, TokenStudioShadowValue, TokenStudioShadowRecord, } from "./types";
9
+ export * as cli from "./cli";
@@ -39,7 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  };
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.cli = exports.flattenTokens = exports.formatValues = exports.resolveReferences = exports.deepMerge = exports.isToken = exports.maybeParseToPx = exports.maybeParseToNumber = exports.mapEntries = exports.kebabCase = exports.simplifyValues = void 0;
42
+ exports.cli = exports.defineTransform = exports.mapKeysRecursively = exports.makeResolver = exports.flattenTokens = exports.formatValues = exports.resolveReferences = exports.deepMerge = exports.isToken = exports.maybeParseToPx = exports.maybeParseToNumber = exports.mapEntries = exports.kebabCase = exports.simplifyValues = void 0;
43
43
  // Export all utility functions
44
44
  var utils_1 = require("./utils");
45
45
  Object.defineProperty(exports, "simplifyValues", { enumerable: true, get: function () { return utils_1.simplifyValues; } });
@@ -52,5 +52,10 @@ Object.defineProperty(exports, "deepMerge", { enumerable: true, get: function ()
52
52
  Object.defineProperty(exports, "resolveReferences", { enumerable: true, get: function () { return utils_1.resolveReferences; } });
53
53
  Object.defineProperty(exports, "formatValues", { enumerable: true, get: function () { return utils_1.formatValues; } });
54
54
  Object.defineProperty(exports, "flattenTokens", { enumerable: true, get: function () { return utils_1.flattenTokens; } });
55
+ Object.defineProperty(exports, "makeResolver", { enumerable: true, get: function () { return utils_1.makeResolver; } });
56
+ Object.defineProperty(exports, "mapKeysRecursively", { enumerable: true, get: function () { return utils_1.mapKeysRecursively; } });
57
+ Object.defineProperty(exports, "defineTransform", { enumerable: true, get: function () { return utils_1.defineTransform; } });
58
+ // Note: Transformers are now provided as examples in the examples/ directory
59
+ // Users should copy and customize these examples for their specific needs
55
60
  // Export the CLI module (for internal use)
56
61
  exports.cli = __importStar(require("./cli"));
@@ -0,0 +1,3 @@
1
+ import { TransformerModule } from "./types";
2
+ declare const ourRequire: (modulePath: string) => Promise<TransformerModule>;
3
+ export { ourRequire };
@@ -0,0 +1,22 @@
1
+ export interface Metadata {
2
+ tokenSetOrder: string[];
3
+ }
4
+ export interface TransformInput {
5
+ metadata: Metadata;
6
+ tokenData: any;
7
+ }
8
+ export interface TransformerModule {
9
+ transform: (input: TransformInput) => any;
10
+ }
11
+ export type TokenStudioShadowValue = {
12
+ color: string;
13
+ x: string;
14
+ y: string;
15
+ blur: string;
16
+ spread: string;
17
+ };
18
+ export type TokenStudioShadowRecord = Record<string, {
19
+ value: TokenStudioShadowValue;
20
+ } | {
21
+ value: TokenStudioShadowValue[];
22
+ }>;
@@ -0,0 +1,60 @@
1
+ import { $IntentionalAny } from "../types";
2
+ import { Metadata } from "./types";
3
+ /**
4
+ * Shared utility functions for tokens-studio transformations
5
+ */
6
+ /**
7
+ * Simplifies nested token values by extracting the 'value' property
8
+ */
9
+ export declare const simplifyValues: (x: {
10
+ value: unknown;
11
+ } | string | null) => $IntentionalAny;
12
+ /**
13
+ * Converts space-separated strings to camelCase
14
+ */
15
+ export declare const kebabCase: (x: string) => string;
16
+ /**
17
+ * Maps over object entries with a custom mapper function
18
+ */
19
+ export declare const mapEntries: <K extends string | number | symbol, V, K2 extends string | number | symbol, V2>(obj: Record<K, V>, mapper: (key: K, value: V) => [K2, V2]) => Record<K2, V2>;
20
+ /**
21
+ * Attempts to parse a string or number to a number, returns original if not possible
22
+ */
23
+ export declare const maybeParseToNumber: (x: string | number) => string | number;
24
+ /**
25
+ * Converts a value to pixels (adds 'px' suffix if it's a number)
26
+ */
27
+ export declare const maybeParseToPx: (x: string | number) => string;
28
+ /**
29
+ * Type guard to check if an object is a token with value and type properties
30
+ */
31
+ export declare const isToken: (obj: any) => obj is {
32
+ value: string;
33
+ type: string;
34
+ };
35
+ /**
36
+ * Deep merge utility for combining token objects
37
+ */
38
+ export declare const deepMerge: (mergedTokens: any, tokenData: any) => void;
39
+ /**
40
+ * Resolves token references (strings like "{path.to.token}") using a value map
41
+ */
42
+ export declare const resolveReferences: (obj: any, map: Map<string, string>) => void;
43
+ /**
44
+ * Formats token values by extracting the value property
45
+ */
46
+ export declare const formatValues: (v: {
47
+ type: string;
48
+ value: string | number;
49
+ }) => string | number;
50
+ export declare const flattenTokens: (v: any, depth: number | undefined, transformKey: (key: string, depth: number) => string) => unknown;
51
+ /** Creates a resolver function for token references */
52
+ export declare const makeResolver: <T extends object>(data: T, keys: (keyof T)[]) => (str: string) => any;
53
+ export declare const mapKeysRecursively: (obj: $IntentionalAny, keyMapper: (key: string) => string) => Record<string, any>;
54
+ export declare const defineTransform: <TInput, TOutput = unknown>(transformFn: (input: {
55
+ tokenData: TInput;
56
+ metadata: Metadata;
57
+ }) => TOutput) => (input: {
58
+ tokenData: TInput;
59
+ metadata: Metadata;
60
+ }) => TOutput;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.flattenTokens = exports.formatValues = exports.resolveReferences = exports.deepMerge = exports.isToken = exports.maybeParseToPx = exports.maybeParseToNumber = exports.mapEntries = exports.kebabCase = exports.simplifyValues = void 0;
3
+ exports.defineTransform = exports.mapKeysRecursively = exports.makeResolver = exports.flattenTokens = exports.formatValues = exports.resolveReferences = exports.deepMerge = exports.isToken = exports.maybeParseToPx = exports.maybeParseToNumber = exports.mapEntries = exports.kebabCase = exports.simplifyValues = void 0;
4
4
  const remeda_1 = require("remeda");
5
5
  /**
6
6
  * Shared utility functions for tokens-studio transformations
@@ -154,3 +154,39 @@ const flattenTokens = (v, depth = 0, transformKey) => {
154
154
  return v;
155
155
  };
156
156
  exports.flattenTokens = flattenTokens;
157
+ /** Creates a resolver function for token references */
158
+ const makeResolver = (data, keys) => {
159
+ const index = {};
160
+ for (const k of keys) {
161
+ for (const [vName, value] of Object.entries(data[k])) {
162
+ if (typeof k !== "string")
163
+ continue;
164
+ index[`${k}.${vName}`] = value;
165
+ }
166
+ }
167
+ return (str) => {
168
+ var _a;
169
+ if (str[0] === "{" && str[str.length - 1] === "}") {
170
+ const path = str.substring(1, str.length - 1);
171
+ return (_a = index[path]) === null || _a === void 0 ? void 0 : _a.value;
172
+ }
173
+ else {
174
+ return str;
175
+ }
176
+ };
177
+ };
178
+ exports.makeResolver = makeResolver;
179
+ const mapKeysRecursively = (obj, keyMapper) => {
180
+ const visitor = (k, v) => {
181
+ if (typeof v === "object") {
182
+ return [keyMapper(k), (0, exports.mapEntries)(v, visitor)];
183
+ }
184
+ else {
185
+ return [keyMapper(k), v];
186
+ }
187
+ };
188
+ return (0, exports.mapEntries)(obj, visitor);
189
+ };
190
+ exports.mapKeysRecursively = mapKeysRecursively;
191
+ const defineTransform = (transformFn) => transformFn;
192
+ exports.defineTransform = defineTransform;
@@ -0,0 +1,2 @@
1
+ export type $IntentionalAny = any;
2
+ export type { CLIModule, CLIModuleConfig } from "./shared/cli";
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ type Args = {
3
+ subcommand: "wait-deployment";
4
+ commit: string;
5
+ interval: number;
6
+ timeout: number;
7
+ team: string;
8
+ project: string;
9
+ };
10
+ declare const _default: import("../shared/cli").CLIModule<Args>;
11
+ export default _default;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export declare function waitForDeploymentReady({ team, project, commitSha, interval, timeout, accessToken, }: {
2
+ team: string;
3
+ project: string;
4
+ commitSha: string;
5
+ interval: number;
6
+ timeout: number;
7
+ accessToken: string;
8
+ }): Promise<import("../shared/deployment-service").Deployment | undefined>;
@@ -0,0 +1,15 @@
1
+ import { IDeploymentService, Deployment } from "../shared/deployment-service";
2
+ export type VercelDeployment = {
3
+ state: string;
4
+ url: string;
5
+ meta: {
6
+ githubCommitSha: string;
7
+ };
8
+ };
9
+ export declare class VercelDeploymentService implements IDeploymentService {
10
+ private teamId;
11
+ private projectId;
12
+ private accessToken;
13
+ constructor(teamId: string, projectId: string, accessToken: string);
14
+ fetchForCommit(commitSha: string): Promise<Deployment[]>;
15
+ }
@@ -0,0 +1,62 @@
1
+ # Tokens Studio Transform Examples
2
+
3
+ This directory contains example transformer handlers that demonstrate how to use the `ixt tokens-studio transform` command with custom transformation logic.
4
+
5
+ ## Development Setup
6
+
7
+ When working in this repository during development, the TypeScript path mappings are configured to resolve `@interactivethings/scripts/*` imports to the local source files.
8
+
9
+ To compile and check the examples:
10
+
11
+ ```bash
12
+ npx tsc --noEmit --project tsconfig.examples.json
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### MUI Transform Example
18
+
19
+ ```bash
20
+ ixt tokens-studio transform --handler ./examples/mui-transform.ts --input ./tokens --output mui-theme.json
21
+ ```
22
+
23
+ ### Tailwind Transform Example
24
+
25
+ ```bash
26
+ ixt tokens-studio transform --handler ./examples/tailwind-transform.ts --input ./tokens --output tailwind-theme.json
27
+ ```
28
+
29
+ ### Custom Transform Example
30
+
31
+ ```bash
32
+ ixt tokens-studio transform --handler ./examples/custom-transform.js --input ./tokens --output custom-theme.json
33
+ ```
34
+
35
+ ## Creating Your Own Transformer
36
+
37
+ 1. Copy one of the example files as a starting point
38
+ 2. Modify the `transform` function to suit your needs
39
+ 3. The `transform` function receives: `{ metadata, tokenData }`
40
+ 4. Return the transformed data object
41
+
42
+ ### Using in Production
43
+
44
+ When using these examples in your own project after installing `@interactivethings/scripts`:
45
+
46
+ ```typescript
47
+ import {
48
+ kebabCase,
49
+ mapEntries /* ... */,
50
+ } from "@interactivethings/scripts/tokens-studio";
51
+ import { $IntentionalAny } from "@interactivethings/scripts/types";
52
+ ```
53
+
54
+ Or using the alias:
55
+
56
+ ```typescript
57
+ import {
58
+ kebabCase,
59
+ mapEntries /* ... */,
60
+ } from "@interactivethings/scripts/tokens-studio";
61
+ import { $IntentionalAny } from "@interactivethings/scripts/types";
62
+ ```
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Example custom transformer handler
3
+ * This file demonstrates how to create a completely custom transformer
4
+ *
5
+ * Usage:
6
+ * ixt tokens-studio transform --handler ./examples/custom-transform.js --input ./tokens --output theme.json
7
+ */
8
+
9
+ module.exports = {
10
+ transform: (input) => {
11
+ const { metadata, tokenData } = input;
12
+
13
+ // Example: Extract only colors and convert to CSS custom properties
14
+ const colors = tokenData.core || {};
15
+
16
+ const cssVariables = {};
17
+
18
+ // Recursively convert tokens to CSS custom property format
19
+ const convertToCssVars = (obj, prefix = "") => {
20
+ for (const [key, value] of Object.entries(obj)) {
21
+ if (value && typeof value === "object" && "value" in value) {
22
+ // This is a token
23
+ cssVariables[`--${prefix}${key}`] = value.value;
24
+ } else if (value && typeof value === "object") {
25
+ // This is a nested object
26
+ convertToCssVars(value, `${prefix}${key}-`);
27
+ }
28
+ }
29
+ };
30
+
31
+ convertToCssVars(colors, "color-");
32
+
33
+ return {
34
+ cssVariables,
35
+ metadata: {
36
+ generatedAt: new Date().toISOString(),
37
+ tokenSetOrder: metadata.tokenSetOrder,
38
+ },
39
+ };
40
+ },
41
+ };
@@ -0,0 +1,33 @@
1
+ import { defineConfig } from '@interactivethings/scripts';
2
+
3
+ export default defineConfig({
4
+ figma: {
5
+ // Figma API token (can also be set via FIGMA_TOKEN environment variable)
6
+ token: process.env.FIGMA_TOKEN,
7
+
8
+ // Assets to download from Figma
9
+ assets: [
10
+ {
11
+ name: "icons",
12
+ url: "https://www.figma.com/design/ElWWZIcOGFhiT06rzfIwRO/Design-System?node-id=11861-10071",
13
+ output: "src/assets/icons"
14
+ },
15
+ {
16
+ name: "illustrations",
17
+ url: "https://www.figma.com/design/ElWWZIcOGFhiT06rzfIwRO/Design-System?node-id=12345-67890",
18
+ output: "src/assets/illustrations"
19
+ }
20
+ ]
21
+ },
22
+
23
+ tokensStudio: {
24
+ // Input directory containing design tokens
25
+ input: "./design-tokens",
26
+
27
+ // Default output file for transformations
28
+ output: "src/theme/tokens.json",
29
+
30
+ // Default transformer handler
31
+ handler: "./transforms/mui-transform.ts"
32
+ }
33
+ });
@@ -0,0 +1,158 @@
1
+ /**
2
+ * MUI transformer for tokens-studio
3
+ * Transforms tokens exported with tokens-studio in Figma into
4
+ * a format that is easier to work with when using MUI.
5
+ */
6
+
7
+ import { mapValues, pick } from "remeda";
8
+
9
+ import { $IntentionalAny } from "@interactivethings/scripts/types";
10
+ import {
11
+ simplifyValues,
12
+ kebabCase,
13
+ mapEntries,
14
+ maybeParseToNumber,
15
+ maybeParseToPx,
16
+ makeResolver,
17
+ defineTransform,
18
+ type TokenStudioShadowValue,
19
+ mapKeysRecursively,
20
+ } from "@interactivethings/scripts/tokens-studio";
21
+
22
+ // Ideally those types should be imported from your theme's type definitions
23
+ // You should change those lines to match your actual theme structure
24
+ // @example
25
+ // ```
26
+ // import tokensStudioTokens from '../path/to/your/tokens-studio/tokens.json';
27
+ // type MUITokenData = typeof tokensStudioTokens
28
+ // ```
29
+ type MUITokenData = {
30
+ Base: $IntentionalAny;
31
+ Functional: $IntentionalAny;
32
+ Elevation: Record<
33
+ string,
34
+ { value: TokenStudioShadowValue | TokenStudioShadowValue[] }
35
+ >;
36
+ Desktop: $IntentionalAny;
37
+ Mobile: $IntentionalAny;
38
+ fontFamilies: $IntentionalAny;
39
+ lineHeights: $IntentionalAny;
40
+ fontWeights: $IntentionalAny;
41
+ fontSize: $IntentionalAny;
42
+ letterSpacing: $IntentionalAny;
43
+ textDecoration: $IntentionalAny;
44
+ };
45
+
46
+ const renameColorKeys = (k: string) => {
47
+ return kebabCase(
48
+ k
49
+ .replace(/-[a-zA-Z0-9]+/, "")
50
+ .replace(" - ", " ")
51
+ .replace(",", "")
52
+ .replace(/^\d+\s+/, "")
53
+ );
54
+ };
55
+
56
+ const getPalette = (tokensData: MUITokenData) => {
57
+ const data = tokensData;
58
+ const colorKeys = ["Base", "Functional"] as const;
59
+
60
+ let palette = pick(data, colorKeys);
61
+ type Palette = typeof palette;
62
+ palette = mapValues(palette, simplifyValues);
63
+ const tpalette = mapKeysRecursively(palette, renameColorKeys) as unknown as {
64
+ base: Palette["Base"];
65
+ functional: Palette["Functional"];
66
+ };
67
+ palette = {
68
+ ...tpalette.base,
69
+ ...pick(tpalette, ["functional"]),
70
+ };
71
+ return palette;
72
+ };
73
+
74
+ const getTypography = (tokensData: MUITokenData) => {
75
+ const sizes = ["Desktop", "Mobile"] as const;
76
+ const res = {} as Record<string, $IntentionalAny>;
77
+ const resolve = makeResolver(tokensData, [
78
+ "fontFamilies",
79
+ "lineHeights",
80
+ "fontWeights",
81
+ "fontSize",
82
+ "letterSpacing",
83
+ "textDecoration",
84
+ ] as const);
85
+
86
+ const cleanupTypographyFns = {
87
+ fontWeight: (x: string) => {
88
+ const lowered = x.toLowerCase();
89
+ if (lowered == "regular") {
90
+ return 400;
91
+ }
92
+ return lowered;
93
+ },
94
+ fontSize: maybeParseToNumber,
95
+ lineHeight: (x: string | number) => maybeParseToPx(maybeParseToNumber(x)),
96
+ paragraphSpacing: maybeParseToNumber,
97
+ letterSpacing: maybeParseToNumber,
98
+ };
99
+
100
+ const cleanupTypographyValue = (k: string, v: $IntentionalAny) => {
101
+ if (k in cleanupTypographyFns) {
102
+ return [
103
+ k,
104
+ cleanupTypographyFns[k as keyof typeof cleanupTypographyFns](v),
105
+ ] as [string, $IntentionalAny];
106
+ } else {
107
+ return [k, v] as [string, $IntentionalAny];
108
+ }
109
+ };
110
+
111
+ for (const size of sizes) {
112
+ const typographies = tokensData[size];
113
+ for (const [typo, typoDataRaw] of Object.entries(typographies)) {
114
+ const typoData = typoDataRaw as { value: $IntentionalAny };
115
+ const typoKey = kebabCase(typo.toLowerCase());
116
+ res[typoKey] = res[typoKey] || {};
117
+ res[typoKey][size.toLowerCase()] = mapEntries(
118
+ mapValues(typoData.value, resolve),
119
+ cleanupTypographyValue
120
+ );
121
+ }
122
+ }
123
+ return res;
124
+ };
125
+
126
+ const getShadows = (tokenData: MUITokenData) => {
127
+ const transformShadow = (shadowData: TokenStudioShadowValue) => {
128
+ const { color, x, y, blur, spread } = shadowData;
129
+ return `${x}px ${y}px ${blur}px ${spread}px ${color}`;
130
+ };
131
+
132
+ const shadows = mapEntries(tokenData.Elevation, (k, v) => {
133
+ const shadowEntry = v as
134
+ | { value: TokenStudioShadowValue }
135
+ | { value: TokenStudioShadowValue[] };
136
+ return [
137
+ `${Number((k as string).replace("dp", "").replace("pd", ""))}`,
138
+ Array.isArray(shadowEntry.value)
139
+ ? (shadowEntry.value as TokenStudioShadowValue[])
140
+ .map(transformShadow)
141
+ .join(", ")
142
+ : transformShadow(shadowEntry.value as TokenStudioShadowValue),
143
+ ];
144
+ });
145
+ return shadows;
146
+ };
147
+
148
+ export const transform = defineTransform<MUITokenData>((input) => {
149
+ const palette = getPalette(input.tokenData);
150
+ const typography = getTypography(input.tokenData);
151
+ const shadows = getShadows(input.tokenData);
152
+
153
+ return {
154
+ palette,
155
+ typography,
156
+ shadows,
157
+ };
158
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Tailwind transformer for tokens-studio
3
+ * Transforms tokens exported with tokens-studio in Figma into
4
+ * a format that is compatible with Tailwind CSS.
5
+ */
6
+
7
+ import { mapValues, pick } from "remeda";
8
+ import {
9
+ resolveReferences,
10
+ mapEntries,
11
+ flattenTokens,
12
+ defineTransform,
13
+ } from "@interactivethings/scripts/tokens-studio";
14
+ import { $IntentionalAny } from "../dist/types";
15
+
16
+ // Ideally those types should be imported from your theme's type definitions
17
+ // You should change those lines to match your actual theme structure
18
+ // @example
19
+ // ```
20
+ // import coreTokens from '../path/to/your/core/tokens.json';
21
+ // import functionalTokens from '../path/to/your/functional/tokens.json';
22
+ // type TailwindTokenData = {
23
+ // core: typeof coreTokens;
24
+ // functional: typeof functionalTokens;
25
+ // };
26
+ // ```
27
+ type TailwindTokenData = {
28
+ core: $IntentionalAny;
29
+ functional: $IntentionalAny;
30
+ };
31
+
32
+ export const transform = defineTransform<TailwindTokenData>((input) => {
33
+ const mergedTokens = input.tokenData;
34
+
35
+ const valueMap = new Map<string, string>();
36
+ resolveReferences(mergedTokens, valueMap);
37
+
38
+ const theme = {
39
+ colors: {
40
+ ...pick(mergedTokens.core, [
41
+ "amber",
42
+ "blue",
43
+ "brand",
44
+ "cyan",
45
+ "emerald",
46
+ "fuchsia",
47
+ "green",
48
+ "indigo",
49
+ "light-blue",
50
+ "lime",
51
+ "midnight",
52
+ "monochrome",
53
+ "orange",
54
+ "pink",
55
+ "purple",
56
+ "red",
57
+ "rose",
58
+ "teal",
59
+ "violet",
60
+ "yellow",
61
+ ]),
62
+ ...pick(mergedTokens.functional, [
63
+ "primary",
64
+ "secondary",
65
+ "tertiary",
66
+ "error",
67
+ "success",
68
+ "warning",
69
+ "link",
70
+ "surface",
71
+ "on-surface",
72
+ "surface-inverted",
73
+ "on-surface-inverted",
74
+ "border",
75
+ "input",
76
+ "accent",
77
+ "qualitative",
78
+ "pathways",
79
+ ]),
80
+ },
81
+ borderWidth: {
82
+ ...mergedTokens.functional["border-width"],
83
+ },
84
+ borderRadius: {
85
+ ...mergedTokens.functional["border-radius"],
86
+ },
87
+ fontSize: mergedTokens.core["font-size"],
88
+ fontWeight: mergedTokens.core["font-weight"],
89
+ lineHeight: mergedTokens.core["line-height"],
90
+ spacing: mergedTokens.functional.spacing,
91
+ };
92
+
93
+ const transformKey = (key: string, depth: number) => {
94
+ if (key.startsWith("on-")) {
95
+ if (depth === 1) {
96
+ return key.replace("on-", "foreground-");
97
+ } else {
98
+ return "foreground";
99
+ }
100
+ }
101
+ if (key === "default") {
102
+ return "DEFAULT";
103
+ }
104
+ return key;
105
+ };
106
+
107
+ const flattenedTheme = mapValues(theme, (x) =>
108
+ flattenTokens(x, 0, transformKey)
109
+ ) as {
110
+ colors: Record<string, string>;
111
+ borderWidth: Record<string, string>;
112
+ borderRadius: Record<string, string>;
113
+ fontSize: Record<
114
+ "mobile-large" | "mobile-xxxlarge",
115
+ Record<string, number>
116
+ >;
117
+ fontWeight: Record<string, string>;
118
+ lineHeight: Record<
119
+ "mobile-large" | "mobile-xxxlarge",
120
+ Record<string, number>
121
+ >;
122
+ spacing: Record<string, string>;
123
+ };
124
+
125
+ const lineHeights = flattenedTheme.lineHeight;
126
+ const fontWeights = flattenedTheme.fontWeight;
127
+
128
+ const finalTheme = {
129
+ ...flattenedTheme,
130
+ borderWidth: mapValues(flattenedTheme.borderWidth, (x) => `${x}px`),
131
+ borderRadius: mapValues(flattenedTheme.borderRadius, (x) => `${x}px`),
132
+ fontSize: mapEntries(
133
+ flattenedTheme.fontSize["mobile-large"],
134
+ (k: string, value) => {
135
+ return [
136
+ k.replace(",", ""),
137
+ [
138
+ `${value}px`,
139
+ {
140
+ lineHeight: lineHeights["mobile-large"][k]
141
+ ? `${lineHeights["mobile-large"][k]}px`
142
+ : undefined,
143
+ fontWeight: fontWeights[k],
144
+ },
145
+ ],
146
+ ];
147
+ }
148
+ ),
149
+ fontWeight: flattenedTheme.fontWeight,
150
+ lineHeight: mapEntries(
151
+ flattenedTheme.lineHeight["mobile-large"],
152
+ (k: string, value) => {
153
+ return [k.replace(",", ""), `${value}px`];
154
+ }
155
+ ),
156
+ backgroundImage: {
157
+ "gradient-2":
158
+ "var(--Gradient-2, linear-gradient(135deg, var(--core-brand-electricblue500, #48A1C7) 20.83%, var(--core-brand-nocturne700, #336476) 87.96%))",
159
+ },
160
+ spacing: mapEntries(flattenedTheme.spacing, (k, v) => [
161
+ k.replace("spacing-", ""),
162
+ `${v}px`,
163
+ ]),
164
+ };
165
+
166
+ return finalTheme;
167
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interactivethings/scripts",
3
- "version": "2.1.4",
3
+ "version": "2.2.1",
4
4
  "main": "dist/index.js",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -36,7 +36,8 @@
36
36
  "files": [
37
37
  "dist",
38
38
  "README.md",
39
- "codemods"
39
+ "codemods",
40
+ "examples"
40
41
  ],
41
42
  "dependencies": {
42
43
  "@next/env": "^15.5.4",