diff --git a/README.md b/README.md index 8f86d4a..58c19c9 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# sketch-assistant-template +# NDS Sketch theme assistant -> 💁‍♀️ This repository contains a starter project for developing a new Assistant. +This Sketch Assistant is to be used when designing theme files for the Natural Design System. The Assistant performs the following checks on your theme file: -👉 -[Click here to generate a new project using this repository as a template](https://github.com/sketch-hq/sketch-assistant-template/generate) +## Rules -## Getting started - -For in-depth guides head over to the [Sketch Developer](https://developer.sketch.com/assistants/) -site. - -- [Getting started](https://developer.sketch.com/assistants/getting-started) -- [Write a rule](https://developer.sketch.com/assistants/write-a-rule) -- [Publishing](https://developer.sketch.com/assistants/publish) -- And more! +* [Duplicate symbols](./src/rules/duplicate-symbols) +* [Duplicate layer styles](./src/rules/duplicate-layer-styles) +* [Duplicate text styles](./src/rules/duplicate-text-styles) +* [Default symbols](./src/rules/default-symbols) +* [Default layer styles](./src/rules/default-layer-styles) +* [Default text styles](./src/rules/default-text-styles) +* [Sync layer styles](./src/rules/sync-layer-styles) +* [Sync text styles](./src/rules/sync-text-styles) +* [Symbol names](./src/rules/symbol-names) +* [Layer style names](./src/rules/layer-style-names) +* [Text style names](./src/rules/text-style-names) +* [Modifier structure](./src/rules/modifier-structure) +* [Embedded fonts](./src/rules/embed-fonts) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 04cdcaa..51d4937 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "sketch-assistant-template", + "name": "nds-sketch-theme-assistant", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/package.json b/package.json index add5c57..cff1fef 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { - "name": "sketch-assistant-template", - "homepage": "https://github.com/sketch-hq/sketch-assistant-template", + "name": "nds-sketch-theme-assistant", + "homepage": "https://github.com/design-ops/nds-sketch-theme-assistant", "version": "1.0.0", "main": "dist/index.js", "sketch": "dist/sketch.js", "license": "MIT", "sketch-assistant": { - "title": "Sketch Assistant Template", - "description": "Template repository for a starter Assistant project", + "title": "NDS Sketch Theme assistant", + "description": "Sketch Theme file assistant for use with the Natural Design System", "icon": "" }, "author": { - "name": "Your Name or Company Name" + "name": "Design Ops" }, "keywords": [ "sketch", @@ -19,7 +19,10 @@ "sketch assistant", "public", "assistant", - "design ops" + "design ops", + "nds", + "natural design system", + "design system" ], "files": [ "dist" diff --git a/src/__tests__/default-layer-styles.sketch b/src/__tests__/default-layer-styles.sketch new file mode 100644 index 0000000..0b64beb Binary files /dev/null and b/src/__tests__/default-layer-styles.sketch differ diff --git a/src/__tests__/default-symbols.sketch b/src/__tests__/default-symbols.sketch new file mode 100644 index 0000000..0ee943a Binary files /dev/null and b/src/__tests__/default-symbols.sketch differ diff --git a/src/__tests__/default-text-styles.sketch b/src/__tests__/default-text-styles.sketch new file mode 100644 index 0000000..4e17d6d Binary files /dev/null and b/src/__tests__/default-text-styles.sketch differ diff --git a/src/__tests__/duplicate-layer-styles.sketch b/src/__tests__/duplicate-layer-styles.sketch new file mode 100644 index 0000000..7b60919 Binary files /dev/null and b/src/__tests__/duplicate-layer-styles.sketch differ diff --git a/src/__tests__/duplicate-symbols.sketch b/src/__tests__/duplicate-symbols.sketch new file mode 100644 index 0000000..2f90a6a Binary files /dev/null and b/src/__tests__/duplicate-symbols.sketch differ diff --git a/src/__tests__/duplicate-text-styles.sketch b/src/__tests__/duplicate-text-styles.sketch new file mode 100644 index 0000000..0f14946 Binary files /dev/null and b/src/__tests__/duplicate-text-styles.sketch differ diff --git a/src/__tests__/embed-fonts.sketch b/src/__tests__/embed-fonts.sketch new file mode 100644 index 0000000..a6af2d2 Binary files /dev/null and b/src/__tests__/embed-fonts.sketch differ diff --git a/src/__tests__/empty.sketch b/src/__tests__/empty.sketch deleted file mode 100644 index 6348a1e..0000000 Binary files a/src/__tests__/empty.sketch and /dev/null differ diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index f130db4..ca48ecf 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -1,13 +1,136 @@ import { resolve } from 'path' -import { testAssistant } from '@sketch-hq/sketch-assistant-utils' +import { testRuleInAssistant } from '@sketch-hq/sketch-assistant-utils' import Assistant from '..' -test('test assistant', async () => { - const { violations, ruleErrors } = await testAssistant( - resolve(__dirname, './empty.sketch'), +test('Duplicate layer styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './duplicate-layer-styles.sketch'), Assistant, + "nds-sketch-theme-assistant/duplicate-layer-styles" ) - expect(violations[0].message).toBe('Hello world') - expect(ruleErrors).toHaveLength(0) + expect(violations[0].message).toBe("'background' has a duplicate layer style") + expect(violations).toHaveLength(1) +}) + +test('Duplicate text styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './duplicate-text-styles.sketch'), + Assistant, + "nds-sketch-theme-assistant/duplicate-text-styles" + ) + expect(violations[0].message).toBe("'title' has a duplicate text style") + expect(violations).toHaveLength(1) +}) + +test('Duplicate symbols', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './duplicate-symbols.sketch'), + Assistant, + "nds-sketch-theme-assistant/duplicate-symbols" + ) + expect(violations[0].message).toBe("'icon' has a duplicate symbol") + expect(violations).toHaveLength(1) +}) + +test('Default layer styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './default-layer-styles.sketch'), + Assistant, + "nds-sketch-theme-assistant/default-layer-styles" + ) + expect(violations[0].message).toBe("'background' is missing a default layer style") + expect(violations).toHaveLength(1) +}) + +test('Default text styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './default-text-styles.sketch'), + Assistant, + "nds-sketch-theme-assistant/default-text-styles" + ) + expect(violations[0].message).toBe("'title' is missing a default text style") + expect(violations).toHaveLength(1) +}) + +test('Default symbols', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './default-symbols.sketch'), + Assistant, + "nds-sketch-theme-assistant/default-symbols" + ) + expect(violations[0].message).toBe("'icon' is missing a default symbol") + expect(violations).toHaveLength(1) +}) + +test('Modifier structure', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './modifier-structure.sketch'), + Assistant, + "nds-sketch-theme-assistant/modifier-structure" + ) + expect(violations[0].message).toBe("'background --radius' modifier has too many (2) layers") + expect(violations[1].message).toBe("'header/background --radius' modifier requires a single layer") + expect(violations).toHaveLength(2) +}) + +test('Sync layer styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './sync-layer-styles.sketch'), + Assistant, + "nds-sketch-theme-assistant/sync-layer-styles" + ) + expect(violations[0].message).toBe("'background' shared style on 'Rectangle' is out of sync") + expect(violations).toHaveLength(1) +}) + +test('Sync text styles', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './sync-text-styles.sketch'), + Assistant, + "nds-sketch-theme-assistant/sync-text-styles" + ) + expect(violations[0].message).toBe("'title' shared style on 'Title' is out of sync") + expect(violations).toHaveLength(1) +}) + +test('Symbol names', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './symbol-names.sketch'), + Assistant, + "nds-sketch-theme-assistant/symbol-names" + ) + expect(violations[0].message).toBe('Woops! Symbol name cannot be a "/"') + expect(violations[1].message).toBe('Woops! Symbol name cannot be left blank') + expect(violations).toHaveLength(2) +}) + +test('Text style names', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './text-style-names.sketch'), + Assistant, + "nds-sketch-theme-assistant/text-style-names" + ) + expect(violations[0].message).toBe('Woops! Text Style name cannot be left blank') + expect(violations).toHaveLength(1) +}) + +test('Layer style names', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './layer-style-names.sketch'), + Assistant, + "nds-sketch-theme-assistant/layer-style-names" + ) + expect(violations[0].message).toBe('Woops! Layer Style name cannot be left blank') + expect(violations).toHaveLength(1) +}) + +test('Embed Font', async () => { + const { violations } = await testRuleInAssistant( + resolve(__dirname, './embed-fonts.sketch'), + Assistant, + "nds-sketch-theme-assistant/embed-fonts" + ) + expect(violations[0].message).toBe("'SF Pro Display' font is not embedded") + expect(violations).toHaveLength(1) }) diff --git a/src/__tests__/layer-style-names.sketch b/src/__tests__/layer-style-names.sketch new file mode 100644 index 0000000..e72832b Binary files /dev/null and b/src/__tests__/layer-style-names.sketch differ diff --git a/src/__tests__/modifier-structure.sketch b/src/__tests__/modifier-structure.sketch new file mode 100644 index 0000000..8ee93e1 Binary files /dev/null and b/src/__tests__/modifier-structure.sketch differ diff --git a/src/__tests__/symbol-names.sketch b/src/__tests__/symbol-names.sketch new file mode 100644 index 0000000..ac1c600 Binary files /dev/null and b/src/__tests__/symbol-names.sketch differ diff --git a/src/__tests__/sync-layer-styles.sketch b/src/__tests__/sync-layer-styles.sketch new file mode 100644 index 0000000..938464a Binary files /dev/null and b/src/__tests__/sync-layer-styles.sketch differ diff --git a/src/__tests__/sync-text-styles.sketch b/src/__tests__/sync-text-styles.sketch new file mode 100644 index 0000000..5f8fd67 Binary files /dev/null and b/src/__tests__/sync-text-styles.sketch differ diff --git a/src/__tests__/text-style-names.sketch b/src/__tests__/text-style-names.sketch new file mode 100644 index 0000000..7226473 Binary files /dev/null and b/src/__tests__/text-style-names.sketch differ diff --git a/src/index.ts b/src/index.ts index 8385d4a..3b7c966 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,52 @@ -import { AssistantPackage, RuleDefinition } from '@sketch-hq/sketch-assistant-types' +import { AssistantPackage } from '@sketch-hq/sketch-assistant-types' -const helloWorld: RuleDefinition = { - rule: async (context) => { - context.utils.report('Hello world') - }, - name: 'sketch-assistant-template/hello-world', - title: 'Hello World', - description: 'Reports a hello world message', -} +import { duplicateLayerStyles } from './rules/duplicate-layer-styles' +import { duplicateTextStyles } from './rules/duplicate-text-styles' +import { duplicateSymbols } from './rules/duplicate-symbols' +import { defaultLayerStyles } from './rules/default-layer-styles' +import { defaultTextStyles } from './rules/default-text-styles' +import { defaultSymbols } from './rules/default-symbols' +import { modifierStructure } from './rules/modifier-structure' +import { syncLayerStyles } from './rules/sync-layer-styles' +import { syncTextStyles } from './rules/sync-text-styles' +import { layerStyleNames } from './rules/layer-style-names' +import { textStyleNames } from './rules/text-style-names' +import { symbolNames } from './rules/symbol-names' +import { embedFonts } from './rules/embed-fonts' const assistant: AssistantPackage = async () => { return { - name: 'sketch-assistant-template', - rules: [helloWorld], + name: 'nds-sketch-theme-assistant', + rules: [ + duplicateLayerStyles, + duplicateTextStyles, + duplicateSymbols, + defaultLayerStyles, + defaultTextStyles, + defaultSymbols, + modifierStructure, + syncLayerStyles, + syncTextStyles, + symbolNames, + layerStyleNames, + textStyleNames, + embedFonts, + ], config: { rules: { - 'sketch-assistant-template/hello-world': { active: true }, + 'nds-sketch-theme-assistant/duplicate-layer-styles': { active: true }, + 'nds-sketch-theme-assistant/duplicate-text-styles': { active: true }, + 'nds-sketch-theme-assistant/duplicate-symbols': { active: true }, + 'nds-sketch-theme-assistant/default-layer-styles': { active: true }, + 'nds-sketch-theme-assistant/default-text-styles': { active: true }, + 'nds-sketch-theme-assistant/default-symbols': { active: true }, + 'nds-sketch-theme-assistant/modifier-structure': { active: true }, + 'nds-sketch-theme-assistant/sync-layer-styles': { active: true }, + 'nds-sketch-theme-assistant/sync-text-styles': { active: true }, + 'nds-sketch-theme-assistant/symbol-names': { active: true }, + 'nds-sketch-theme-assistant/layer-style-names': { active: true }, + 'nds-sketch-theme-assistant/text-style-names': { active: true }, + 'nds-sketch-theme-assistant/embed-fonts': { active: true }, }, }, } diff --git a/src/rules/default-layer-styles/README.md b/src/rules/default-layer-styles/README.md new file mode 100644 index 0000000..7ed2524 --- /dev/null +++ b/src/rules/default-layer-styles/README.md @@ -0,0 +1,13 @@ +# ```default-layer-styles``` + +## Locates missing default layer styles in your theme file + +In the Natural Design System everything requires a default state, which is used when no match is determined. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/default-layer-styles/index.ts b/src/rules/default-layer-styles/index.ts new file mode 100644 index 0000000..e72dddf --- /dev/null +++ b/src/rules/default-layer-styles/index.ts @@ -0,0 +1,46 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const defaultLayerStyles: RuleDefinition = { + rule: async (context) => { + + interface Tokens { + token: string + path: string + default: boolean + object: SketchFileObject + } + + var tokens: Array = []; + var tokenParts: Array = []; + var defaultTokens: Array = []; + + for (const style of context.utils.objects.sharedStyle) { + if (style.value.textStyle == undefined) { + + tokenParts = style.name.split('/'); + var tokenName = tokenParts.slice(-1).toString(); + var tokenPath = tokenParts.pop(); + tokenPath = tokenParts.join('/'); + if (tokenParts.length > 0) { + tokens.push({ token: tokenName, path: tokenPath, default: false, object:style }); + } else { + tokens.push({ token: tokenName, path: tokenPath, default: true, object:style }); + } + + } + } + defaultTokens = (tokens.filter((element) => element.default == true)); + + for (const token of tokens) { + var existingElement = defaultTokens.find((element) => element.token == token.token); + if (existingElement == null) { + context.utils.report('\'' + token.token + '\' is missing a default layer style', token.object); + } + + } + + }, + name: 'nds-sketch-theme-assistant/default-layer-styles', + title: 'Default Layer Styles', + description: 'Reports missing default layer style in your NDS theme file.', +} diff --git a/src/rules/default-symbols/README.md b/src/rules/default-symbols/README.md new file mode 100644 index 0000000..dd4bccf --- /dev/null +++ b/src/rules/default-symbols/README.md @@ -0,0 +1,13 @@ +# ```default-symbols``` + +## Locates missing default symbols in your theme file + +In the Natural Design System everything requires a default state, which is used when no match is determined. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/default-symbols/index.ts b/src/rules/default-symbols/index.ts new file mode 100644 index 0000000..7b79264 --- /dev/null +++ b/src/rules/default-symbols/index.ts @@ -0,0 +1,45 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const defaultSymbols: RuleDefinition = { + rule: async (context) => { + + interface Tokens { + token: string + path: string + default: boolean + object: SketchFileObject + } + + var tokens: Array = []; + var tokenParts: Array = []; + var defaultTokens: Array = []; + + for (const symbol of context.utils.objects.symbolMaster) { + + if (symbol.name != null) { + tokenParts = symbol.name.split('/'); + var tokenName = tokenParts.slice(-1).toString(); + var tokenPath = tokenParts.pop(); + tokenPath = tokenParts.join('/'); + if (tokenParts.length > 0) { + tokens.push({ token: tokenName, path: tokenPath, default: false, object:symbol }); + } else { + tokens.push({ token: tokenName, path: tokenPath, default: true, object:symbol }); + } + } + } + defaultTokens = (tokens.filter((element) => element.default == true)); + + for (const token of tokens) { + var existingElement = defaultTokens.find((element) => element.token == token.token); + if (existingElement == null) { + context.utils.report('\'' + token.token + '\' is missing a default symbol', token.object); + } + + } + + }, + name: 'nds-sketch-theme-assistant/default-symbols', + title: 'Default Symbols', + description: 'Reports missing default symbols in your NDS theme file.', +} diff --git a/src/rules/default-text-styles/README.md b/src/rules/default-text-styles/README.md new file mode 100644 index 0000000..be8de26 --- /dev/null +++ b/src/rules/default-text-styles/README.md @@ -0,0 +1,13 @@ +# ```default-text-styles``` + +## Locates missing default text styles in your theme file + +In the Natural Design System, everything requires a default state, which is used when no match is determined. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/default-text-styles/index.ts b/src/rules/default-text-styles/index.ts new file mode 100644 index 0000000..7a649c8 --- /dev/null +++ b/src/rules/default-text-styles/index.ts @@ -0,0 +1,46 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const defaultTextStyles: RuleDefinition = { + rule: async (context) => { + + interface Tokens { + token: string + path: string + default: boolean + object: SketchFileObject + } + + var tokens: Array = []; + var tokenParts: Array = []; + var defaultTokens: Array = []; + + for (const style of context.utils.objects.sharedStyle) { + if (style.value.textStyle != undefined) { + + tokenParts = style.name.split('/'); + var tokenName = tokenParts.slice(-1).toString(); + var tokenPath = tokenParts.pop(); + tokenPath = tokenParts.join('/'); + if (tokenParts.length > 0) { + tokens.push({ token: tokenName, path: tokenPath, default: false, object:style }); + } else { + tokens.push({ token: tokenName, path: tokenPath, default: true, object:style }); + } + + } + } + defaultTokens = (tokens.filter((element) => element.default == true)); + + for (const token of tokens) { + var existingElement = defaultTokens.find((element) => element.token == token.token); + if (existingElement == null) { + context.utils.report('\'' + token.token + '\' is missing a default text style', token.object); + } + + } + + }, + name: 'nds-sketch-theme-assistant/default-text-styles', + title: 'Default Text Styles', + description: 'Reports missing default text styles in your NDS theme file.', +} diff --git a/src/rules/duplicate-layer-styles/README.md b/src/rules/duplicate-layer-styles/README.md new file mode 100644 index 0000000..98fe7f6 --- /dev/null +++ b/src/rules/duplicate-layer-styles/README.md @@ -0,0 +1,13 @@ +# ```duplicate-layer-styles``` + +## Locates duplicate layer styles in your theme file + +In the Natural Design System, everything is unique, so no two same layer styles can co-exist. Duplicates are determined by their name. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/duplicate-layer-styles/index.ts b/src/rules/duplicate-layer-styles/index.ts new file mode 100644 index 0000000..f73e852 --- /dev/null +++ b/src/rules/duplicate-layer-styles/index.ts @@ -0,0 +1,32 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const duplicateLayerStyles: RuleDefinition = { + rule: async (context) => { + interface Duplicate { + name: string + number: number + object: SketchFileObject + } + + var duplicates: Array = []; + var totalDuplicates: Array = []; + + for (const style of context.utils.objects.sharedStyle) { + if (style.value.textStyle == undefined) { + var existingElement = duplicates.find((element) => element.name == style.name) + if (existingElement != null) + existingElement.number++; + else { + duplicates.push({ name: style.name, number: 1, object:style }); + } + } + } + + totalDuplicates = duplicates.filter((element) => element.number > 1); + totalDuplicates.forEach(element => context.utils.report('\'' + element.name + '\' has a duplicate layer style', element.object)); + + }, + name: 'nds-sketch-theme-assistant/duplicate-layer-styles', + title: 'Duplicate Layer Styles', + description: 'Reports duplicate layer styles in your theme file.', +} diff --git a/src/rules/duplicate-symbols/README.md b/src/rules/duplicate-symbols/README.md new file mode 100644 index 0000000..29c857c --- /dev/null +++ b/src/rules/duplicate-symbols/README.md @@ -0,0 +1,13 @@ +# ```duplicate-symbol``` + +## Locates duplicate symbols in your theme file + +In the Natural Design System, everything is unique, so no two same symbols can co-exist. Duplicates are determined by their name. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/duplicate-symbols/index.ts b/src/rules/duplicate-symbols/index.ts new file mode 100644 index 0000000..2ee80c2 --- /dev/null +++ b/src/rules/duplicate-symbols/index.ts @@ -0,0 +1,30 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const duplicateSymbols: RuleDefinition = { + rule: async (context) => { + interface Duplicate { + name: string + number: number + object: SketchFileObject + } + + var duplicates: Array = []; + var totalDuplicates: Array = []; + + for (const symbol of context.utils.objects.symbolMaster) { + var existingElement = duplicates.find((element) => element.name == symbol.name); + if (existingElement != null) + existingElement.number++; + else + duplicates.push({ name: symbol.name, number: 1, object:symbol }); + } + + totalDuplicates = (duplicates.filter((element) => element.number > 1)); + + totalDuplicates.forEach(element => context.utils.report('\'' + element.name + '\' has a duplicate symbol', element.object)); + + }, + name: 'nds-sketch-theme-assistant/duplicate-symbols', + title: 'Duplicate Symbols', + description: 'Reports duplicate symbols in your NDS theme file.', +} diff --git a/src/rules/duplicate-text-styles/README.md b/src/rules/duplicate-text-styles/README.md new file mode 100644 index 0000000..da488f5 --- /dev/null +++ b/src/rules/duplicate-text-styles/README.md @@ -0,0 +1,13 @@ +# ```duplicate-text-styles``` + +## Locates duplicate text styles in your theme file + +In the Natural Design System, everything is unique, so no two same text styles can co-exist. Duplicates are determined by their name. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/duplicate-text-styles/index.ts b/src/rules/duplicate-text-styles/index.ts new file mode 100644 index 0000000..4bcb01b --- /dev/null +++ b/src/rules/duplicate-text-styles/index.ts @@ -0,0 +1,33 @@ +import { RuleDefinition, SketchFileObject } from '@sketch-hq/sketch-assistant-types' + +export const duplicateTextStyles: RuleDefinition = { + rule: async (context) => { + interface Duplicate { + name: string + number: number + object: SketchFileObject + } + + var duplicates: Array = []; + var totalDuplicates: Array = []; + + for (const style of context.utils.objects.sharedStyle) { + if (style.value.textStyle != undefined) { + var existingElement = duplicates.find((element) => element.name == style.name) + if (existingElement != null) + existingElement.number++; + else { + duplicates.push({ name: style.name, number: 1, object:style }); + } + } + } + + totalDuplicates = (duplicates.filter((element) => element.number > 1)); + + totalDuplicates.forEach(element => context.utils.report('\'' + element.name + '\' has a duplicate text style', element.object)); + + }, + name: 'nds-sketch-theme-assistant/duplicate-text-styles', + title: 'Duplicate Text Style', + description: 'Reports duplicate text styles in your NDS theme file.', +} diff --git a/src/rules/embed-fonts/README.md b/src/rules/embed-fonts/README.md new file mode 100644 index 0000000..da1b326 --- /dev/null +++ b/src/rules/embed-fonts/README.md @@ -0,0 +1,13 @@ +# ```embed-fonts``` + +## Notifies if any non-system font is not embedded in the document + +In the Natural Design System embedded fonts are preferred. This is to ensure future work on the theme file even if the font is not present on the user's system. [Read more about embedding fonts in Sketch](https://www.sketch.com/docs/text/#embedding-fonts). + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/embed-fonts/index.ts b/src/rules/embed-fonts/index.ts new file mode 100644 index 0000000..c626cb5 --- /dev/null +++ b/src/rules/embed-fonts/index.ts @@ -0,0 +1,14 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' + +export const embedFonts: RuleDefinition = { + rule: async (context) => { + for (const font of context.utils.objects.fontReference) { + if (font.options == 2) { // Non system font is used, but not embedded + context.utils.report("\'" + font.fontFamilyName + "\' font is not embedded"); + } + } + }, + name: 'nds-sketch-theme-assistant/embed-fonts', + title: 'Embed Fonts', + description: 'Reports if a font family is not embedded in your NDS theme file.', +} diff --git a/src/rules/layer-style-names/README.md b/src/rules/layer-style-names/README.md new file mode 100644 index 0000000..17645dc --- /dev/null +++ b/src/rules/layer-style-names/README.md @@ -0,0 +1,13 @@ +# ```layer-style-names``` + +## Notifies if a layer style is not properly named + +In the Natural Design System layer styles are required to have unique names. This assistant locates any layer styles that are named either `null`, `Undefined` or `blank`. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/layer-style-names/index.ts b/src/rules/layer-style-names/index.ts new file mode 100644 index 0000000..a6c3eef --- /dev/null +++ b/src/rules/layer-style-names/index.ts @@ -0,0 +1,20 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' + +export const layerStyleNames: RuleDefinition = { + rule: async (context) => { + + for (const sharedStyle of context.utils.objects.sharedStyle) { + if (sharedStyle.value.textStyle == undefined) { + + if (sharedStyle.name == null || sharedStyle.name == " ") { + context.utils.report('Woops! Layer Style name cannot be left blank', sharedStyle); + } + + } + } + + }, + name: 'nds-sketch-theme-assistant/layer-style-names', + title: 'Layer Style Names', + description: 'Reports an improperly named layer style in your NDS theme file.', +} diff --git a/src/rules/modifier-structure/README.md b/src/rules/modifier-structure/README.md new file mode 100644 index 0000000..1b647ee --- /dev/null +++ b/src/rules/modifier-structure/README.md @@ -0,0 +1,13 @@ +# ```modifier-structure``` + +## Notifies if a modifier is not properly structured (does not have only one layer) + +In the Natural Design System modifier symbols are used to apply styles that are not present in the style options. Any modifier used in the theme file is required to contain only one layer. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/modifier-structure/index.ts b/src/rules/modifier-structure/index.ts new file mode 100644 index 0000000..65aaef0 --- /dev/null +++ b/src/rules/modifier-structure/index.ts @@ -0,0 +1,24 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' + +export const modifierStructure: RuleDefinition = { + rule: async (context) => { + + for (const symbol of context.utils.objects.symbolMaster) { + if (symbol.name != null && symbol.name.match(/.*? \-\-[^\s]+$/gm)) { // match naming scheme ' --modifier' + + var numberOfLayers = symbol.layers.length; + if (numberOfLayers > 1) { + context.utils.report('\'' + symbol.name + '\' modifier has too many (' + numberOfLayers + ') layers', symbol); + } + if (numberOfLayers < 1 ) { + context.utils.report('\'' + symbol.name + '\' modifier requires a single layer', symbol); + } + + } + } + + }, + name: 'nds-sketch-theme-assistant/modifier-structure', + title: 'Modifier Structure', + description: 'Reports if a modifier has more than one or less than one layer.', +} diff --git a/src/rules/symbol-names/README.md b/src/rules/symbol-names/README.md new file mode 100644 index 0000000..c6aba54 --- /dev/null +++ b/src/rules/symbol-names/README.md @@ -0,0 +1,15 @@ +# ```symbol-names``` + +## Notifies if a symbol is not properly named + +In the Natural Design System layer styles are required to have unique names. This assistant locates any symbols that are named either `null`, `Undefined` or `blank`. + +In Sketch, the `/` is used to create structure and organise symbols, that is why no symbol can be named with a simple `/`. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/symbol-names/index.ts b/src/rules/symbol-names/index.ts new file mode 100644 index 0000000..594c205 --- /dev/null +++ b/src/rules/symbol-names/index.ts @@ -0,0 +1,17 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' + +export const symbolNames: RuleDefinition = { + rule: async (context) => { + for (const symbol of context.utils.objects.symbolMaster) { + if (symbol.name == null || symbol.name == " ") { + context.utils.report('Woops! Symbol name cannot be left blank', symbol); + } + if (symbol.name == "/") { + context.utils.report('Woops! Symbol name cannot be a "/"', symbol); + } + } + }, + name: 'nds-sketch-theme-assistant/symbol-names', + title: 'Symbol Names', + description: 'Reports an improperly named symbol in your NDS theme file.', +} diff --git a/src/rules/sync-layer-styles/README.md b/src/rules/sync-layer-styles/README.md new file mode 100644 index 0000000..24b7379 --- /dev/null +++ b/src/rules/sync-layer-styles/README.md @@ -0,0 +1,13 @@ +# ```sync-layer-styles``` + +## Notifies if a shape layer is out of sync with it's layer style + +If you have made changes to a shape layer but have not yet updated the layer style, this rule will let you know. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/sync-layer-styles/index.ts b/src/rules/sync-layer-styles/index.ts new file mode 100644 index 0000000..7297c72 --- /dev/null +++ b/src/rules/sync-layer-styles/index.ts @@ -0,0 +1,34 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' +import FileFormat from '@sketch-hq/sketch-file-format-ts' + +export const syncLayerStyles: RuleDefinition = { + rule: async (context) => { + + const IGNORE_CLASSES = ['artboard', 'page', 'symbolMaster', 'text'] + type StyleId = string + + const { utils } = context + const sharedStyles: Map = new Map() + + for (const sharedStyle of utils.objects.sharedStyle) { + if (typeof sharedStyle.do_objectID === 'string') { + sharedStyles.set(sharedStyle.do_objectID, sharedStyle) + } + } + + for (const layer of utils.objects.anyLayer) { + if (IGNORE_CLASSES.includes(layer._class)) continue // Ignore certain classes + if (layer._class === 'group' && !layer.style?.shadows?.length) continue // Ignore groups with default styles + if (typeof layer.sharedStyleID !== 'string') continue // Ignore if no shared style id + const sharedStyle = sharedStyles.get(layer.sharedStyleID) + if (!sharedStyle) continue // Ignore if shared style not found + if (!layer.style || !utils.styleEq(layer.style, sharedStyle.value)) { + context.utils.report('\''+ sharedStyle.name + '\' shared style on \''+ layer.name +'\' is out of sync', layer) + } + } + + }, + name: 'nds-sketch-theme-assistant/sync-layer-styles', + title: 'Layer Style is Out of Sync', + description: 'Reports if a layer style is out of sync with the shared style.', +} diff --git a/src/rules/sync-text-styles/README.md b/src/rules/sync-text-styles/README.md new file mode 100644 index 0000000..dac2185 --- /dev/null +++ b/src/rules/sync-text-styles/README.md @@ -0,0 +1,13 @@ +# ```sync-text-styles``` + +## Notifies if a text layer is out of sync with it's text style + +If you have made changes to a text layer but have not yet updated the text style, this rule will let you know. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/sync-text-styles/index.ts b/src/rules/sync-text-styles/index.ts new file mode 100644 index 0000000..d72fe68 --- /dev/null +++ b/src/rules/sync-text-styles/index.ts @@ -0,0 +1,30 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' +import FileFormat from '@sketch-hq/sketch-file-format-ts' + +export const syncTextStyles: RuleDefinition = { + rule: async (context) => { + + type StyleId = string + const { utils } = context + const sharedStyles: Map = new Map() + + for (const sharedStyle of utils.objects.sharedStyle) { + if (typeof sharedStyle.do_objectID === 'string') { + sharedStyles.set(sharedStyle.do_objectID, sharedStyle) + } + } + + for (const text of utils.objects.text) { + if (typeof text.sharedStyleID !== 'string') continue + const sharedStyle = sharedStyles.get(text.sharedStyleID) + if (!sharedStyle) continue + if (!utils.textStyleEq(text.style, sharedStyle.value)) { + context.utils.report('\''+ sharedStyle.name + '\' shared style on \''+ text.name +'\' is out of sync', text) + } + } + + }, + name: 'nds-sketch-theme-assistant/sync-text-styles', + title: 'Text Style is Out of Sync', + description: 'Reports if a text style is out of sync with the shared style.', +} diff --git a/src/rules/text-style-names/README.md b/src/rules/text-style-names/README.md new file mode 100644 index 0000000..8d70ba1 --- /dev/null +++ b/src/rules/text-style-names/README.md @@ -0,0 +1,13 @@ +# ```text-style-names``` + +## Notifies if a text style is not properly named + +In the Natural Design System text styles are required to have unique names. This assistant locates any text styles that are named either `null`, `Undefined` or `blank`. + +## Configuration + +```js +{ + "active": true, +} +``` \ No newline at end of file diff --git a/src/rules/text-style-names/index.ts b/src/rules/text-style-names/index.ts new file mode 100644 index 0000000..d0e6f7e --- /dev/null +++ b/src/rules/text-style-names/index.ts @@ -0,0 +1,16 @@ +import { RuleDefinition } from '@sketch-hq/sketch-assistant-types' + +export const textStyleNames: RuleDefinition = { + rule: async (context) => { + for (const sharedStyle of context.utils.objects.sharedStyle) { + if (sharedStyle.value.textStyle != undefined) { + if (sharedStyle.name == null || sharedStyle.name == " ") { + context.utils.report('Woops! Text Style name cannot be left blank', sharedStyle); + } + } + } + }, + name: 'nds-sketch-theme-assistant/text-style-names', + title: 'Text Style Names', + description: 'Reports an improperly named text style in your NDS theme file.', +}