Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
feat(Themes): Checking and fixing of themes
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome authored and alex-ketch committed Mar 4, 2020
1 parent e80e2e7 commit cc4f4c1
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 38 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ branches:
- 'next'

script:
- npr run check:themes
- npm run lint
- npm run test:unit -- --coverage
- '[[ ! -z "$SAUCE_ACCESS_KEY" ]] && travis_wait npm run test:visual || true'
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"update": "npm run update:selectors && npm run update:examples && npm run update:themes && npm run update:extensions && npm run docs",
"update:selectors": "ts-node --files src/scripts/selectors.ts",
"update:examples": "ts-node --files src/scripts/examples.ts",
"create:theme": "ts-node --files src/scripts/themes.ts create",
"update:extensions": "ts-node --files src/scripts/extensions.ts update",
"update:themes": "ts-node --files src/scripts/themes.ts update",
"create:extension": "ts-node --files src/scripts/extensions.ts create",
"update:extensions": "ts-node --files src/scripts/extensions.ts update",
"create:theme": "ts-node --files src/scripts/themes.ts create",
"check:theme": "ts-node --files src/scripts/themes.ts check",
"check:themes": "ts-node --files src/scripts/themes.ts check '*'",
"build": "npm run build:browser && npm run build:lib",
"build:browser": "webpack --mode production && tsc --emitDeclarationOnly --project tsconfig.browser.json",
"build:lib": "tsc --project tsconfig.lib.json",
Expand Down
177 changes: 141 additions & 36 deletions src/scripts/themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import globby from 'globby'
import path from 'path'

if (module.parent === null) {
const [func, arg1] = process.argv.slice(2)
const [func, arg1, arg2] = process.argv.slice(2)
if (func === 'create') create(arg1)
else if (func === 'check') process.exit(check(arg1, arg2 === 'fix'))
else if (func === 'update') update()
else console.error(`Unrecognised function: ${func}`)
}
Expand All @@ -20,44 +21,159 @@ if (module.parent === null) {
function create(name?: string): void {
// Check that a name has been supplied
if (name === undefined) {
console.log(`You must supply a theme name`)
console.error(`Please supply a theme name`)
process.exit(1)
}

const themeDir = path.join(__dirname, '..', 'themes', name)

// Check that the theme does not already exist
const themeDir = path.join(__dirname, '..', 'themes', name)
if (fs.existsSync(themeDir)) {
console.log(`Theme "${name}" already exists: ${themeDir}`)
process.exit(1)
} else {
fs.mkdirSync(themeDir)
}

// Create necessary files
fs.writeFileSync(
path.join(themeDir, 'README.md'),
`# ${name[0].toUpperCase()}${name.slice(1)}
// Check with `fix: true`, to create necessary files
check(name, true)

// Update `themes/index.ts` etc
update()
}

/**
* Check that a theme has the necessary files.
*
* If the the theme `name` is `*`, will check all themes.
* Use `fix` to fix errors for some checks.
*
* Run using `npm run check:theme -- <theme> [fix]`.
*/
function check(name?: string, fix = false): number {
const themesDir = path.join(__dirname, '..', 'themes')

if (name === undefined) {
console.error(`Please supply a theme name`)
process.exit(1)
}
if (name === '*') {
return globby
.sync('*', {
onlyDirectories: true,
cwd: themesDir
})
.reduce((sum, theme) => sum + check(theme, fix), 0)
}

const messages: string[] = []
const error = (message: string): number => messages.push(message)

// Check that theme dir exists
const themeDir = path.join(themesDir, name)
if (!fs.existsSync(themeDir)) {
if (fix) fs.mkdirSync(themeDir)
else {
error(`Theme directory does not exist: ${themeDir}`)
}
}

// Check that README.md exists
const readme = path.join(themeDir, 'README.md')
if (!fs.existsSync(readme)) {
if (fix) {
fs.writeFileSync(
readme,
`# ${name[0].toUpperCase()}${name.slice(1)}
<!-- Add a description of your theme and notes for contributors. -->\n`
)
)
} else {
error(`Theme README.md does not exist: ${readme}`)
}
}

fs.writeFileSync(
path.join(themeDir, 'index.ts'),
`// Import any extensions and do any DOM manipulation that your theme needs in this file\n`
)
// Check that styles.css exists
const styles = path.join(themeDir, 'styles.css')
if (!fs.existsSync(styles)) {
if (fix) {
fs.writeFileSync(styles, `/* Add your theme's styles to this file */\n`)
} else {
error(`Theme styles file does not exist: ${styles}`)
}
}

fs.writeFileSync(
path.join(themeDir, 'styles.css'),
`/* Add your theme's styles to this file */\n`
)
// Check that index.{ts,js} exists
const indexGlob = path.join(themeDir, 'index.{ts,js}')
const indexFiles = globby.sync(indexGlob)
let indexFile = indexFiles[0]
if (indexFile === undefined) {
if (fix) {
indexFile = path.join(themeDir, 'index.ts')
fs.writeFileSync(
indexFile,
`// Import any extensions and do any DOM manipulation that your theme needs in this file\n`
)
} else error(`Theme script file does not exist: ${indexGlob}`)
} else if (indexFiles.length > 1)
error(`Theme has more than one script file matching: ${indexGlob}`)

// Update `themes.ts` etc
update()
// Check that any other themes or extensions included in CSS, are also
// included in scripts
if (indexFile !== undefined) {
const script = fs.readFileSync(indexFile, 'utf8')
const css = fs.readFileSync(styles, 'utf8')
const imports: string[] = []

// Base theme
{
const cssRegex = /^@import\s+'\.\.\/([\w-]+)\/styles\.css'/gm
let cssMatch
let cssMatches = 0
while ((cssMatch = cssRegex.exec(css)) !== null) {
cssMatches += 1
if (cssMatches === 1) {
const theme = cssMatch[1]
const scriptRegex = new RegExp(`^import\\s+'\\.\\.\\/${theme}'`, 'm')
if (!scriptRegex.test(script)) {
if (fix) imports.push(`import '../${theme}'`)
else
error(
`Theme script file does not import base theme "${theme}": ${indexFile}`
)
}
} else error(`Theme extends more than one base theme: ${styles}`)
}
}

// Extensions
{
const cssRegex = /^@import\s+'\.\.\/\.\.\/extensions\/([\w-]+)\/styles\.css'/gm
let cssMatch
while ((cssMatch = cssRegex.exec(css)) !== null) {
const extension = cssMatch[1]
const scriptRegex = new RegExp(
`^import\\s+'\\.\\.\\/\\.\\.\\/extensions\\/${extension}'`,
'm'
)
if (!scriptRegex.test(script)) {
if (fix) imports.push(`import '../../extensions/${extension}'`)
else
error(
`Theme script file does not import extension "${extension}": ${indexFile}`
)
}
}
}

if (imports.length > 0) {
fs.writeFileSync(indexFile, imports.join('\n') + '\n' + script)
}
}

messages.forEach(message => console.error(message))
return messages.length
}

/**
* Generate `../themes/themes.ts`.
* Generate `../themes/index.ts`.
*
* Run using `npm run update:themes`.
*
Expand All @@ -73,21 +189,10 @@ function update(): void {
cwd: themesDir
})

// Check each theme has the necessary fules
themes.forEach(theme => {
;['README.md', 'styles.css', ['index.js', 'index.ts']].forEach(file => {
const files = globby.sync(file, {
onlyFiles: true,
cwd: path.join(themesDir, theme)
})
if (files.length !== 1) {
console.error(`Theme "${theme}" must have one "${file}" file`)
process.exit(1)
}
})
})
// Lint each theme
themes.forEach(theme => check(theme, true))

// Write all themes to `themes.ts`
// Write list
fs.writeFileSync(
path.join(__dirname, '..', 'themes', 'index.ts'),
`// Generated by scripts/${path.basename(__filename)}. Do not edit.
Expand Down

0 comments on commit cc4f4c1

Please sign in to comment.