Skip to content

Commit

Permalink
Validate that generate path points to a valid theme directories
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmengo committed Dec 12, 2024
1 parent 30eb0d9 commit ff4e09b
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 6 deletions.
9 changes: 6 additions & 3 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1904,10 +1904,11 @@ Creates and adds a new block file to your local theme directory

```
USAGE
$ shopify theme generate block [-n <value>] [--no-color] [--path <value>] [-t text|image|video|product|collection]
$ shopify theme generate block [-f] [-n <value>] [--no-color] [--path <value>] [-t text|image|video|product|collection]
[--verbose]
FLAGS
-f, --force Overwrite existing files
-n, --name=<value> Name of the block
-t, --type=<option> Type of block to generate
<options: text|image|video|product|collection>
Expand All @@ -1932,10 +1933,11 @@ Creates and adds a new section file to your local theme directory

```
USAGE
$ shopify theme generate section [-n <value>] [--no-color] [--path <value>] [-t
$ shopify theme generate section [-f] [-n <value>] [--no-color] [--path <value>] [-t
featured-collection|image-with-text|rich-text|custom] [--verbose]
FLAGS
-f, --force Overwrite existing files
-n, --name=<value> Name of the section
-t, --type=<option> Type of section to generate
<options: featured-collection|image-with-text|rich-text|custom>
Expand All @@ -1961,10 +1963,11 @@ Creates and adds a new template file to your local theme directory

```
USAGE
$ shopify theme generate template [-n <value>] [--no-color] [--path <value>] [-t
$ shopify theme generate template [-f] [-n <value>] [--no-color] [--path <value>] [-t
product|collection|page|blog|article|custom] [--verbose]
FLAGS
-f, --force Overwrite existing files
-n, --name=<value> Name of the template
-t, --type=<option> Type of template to generate
<options: product|collection|page|blog|article|custom>
Expand Down
24 changes: 24 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5326,6 +5326,14 @@
"description": "Creates a new \"theme block\" (https://shopify.dev/docs/themes/architecture/blocks) in your local theme directory.\n\n The block is created in the `blocks` directory with the basic structure needed, including schema and settings.\n\n You can specify the type of block to generate using the `--type` flag. The block will be created with appropriate default settings based on the type.",
"descriptionWithMarkdown": "Creates a new [theme block](https://shopify.dev/docs/themes/architecture/blocks) in your local theme directory.\n\n The block is created in the `blocks` directory with the basic structure needed, including schema and settings.\n\n You can specify the type of block to generate using the `--type` flag. The block will be created with appropriate default settings based on the type.",
"flags": {
"force": {
"allowNo": false,
"char": "f",
"description": "Overwrite existing files",
"env": "SHOPIFY_FLAG_THEME_GENERATE_BLOCK_FORCE",
"name": "force",
"type": "boolean"
},
"name": {
"char": "n",
"description": "Name of the block",
Expand Down Expand Up @@ -5396,6 +5404,14 @@
"description": "Creates a new \"theme section\" (https://shopify.dev/docs/themes/architecture/sections) in your local theme directory.\n\n The section is created in the `sections` directory with the basic structure needed, including schema, settings, and blocks.\n\n You can specify the type of section to generate using the `--type` flag. The section will be created with appropriate default settings and blocks based on the type.",
"descriptionWithMarkdown": "Creates a new [theme section](https://shopify.dev/docs/themes/architecture/sections) in your local theme directory.\n\n The section is created in the `sections` directory with the basic structure needed, including schema, settings, and blocks.\n\n You can specify the type of section to generate using the `--type` flag. The section will be created with appropriate default settings and blocks based on the type.",
"flags": {
"force": {
"allowNo": false,
"char": "f",
"description": "Overwrite existing files",
"env": "SHOPIFY_FLAG_THEME_GENERATE_SECTION_FORCE",
"name": "force",
"type": "boolean"
},
"name": {
"char": "n",
"description": "Name of the section",
Expand Down Expand Up @@ -5465,6 +5481,14 @@
"description": "Creates a new \"theme template\" (https://shopify.dev/docs/themes/architecture/templates) in your local theme directory.\n\n The template is created in the `templates` directory with the basic structure needed, including layout and content sections.\n\n You can specify the type of template to generate using the `--type` flag. The template will be created with appropriate default sections based on the type.",
"descriptionWithMarkdown": "Creates a new [theme template](https://shopify.dev/docs/themes/architecture/templates) in your local theme directory.\n\n The template is created in the `templates` directory with the basic structure needed, including layout and content sections.\n\n You can specify the type of template to generate using the `--type` flag. The template will be created with appropriate default sections based on the type.",
"flags": {
"force": {
"allowNo": false,
"char": "f",
"description": "Overwrite existing files",
"env": "SHOPIFY_FLAG_THEME_GENERATE_TEMPLATE_FORCE",
"name": "force",
"type": "boolean"
},
"name": {
"char": "n",
"description": "Name of the template",
Expand Down
39 changes: 39 additions & 0 deletions packages/theme/src/cli/commands/theme/generate/block.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import GenerateBlock from './block.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {describe, expect, test, vi} from 'vitest'
import {renderWarning} from '@shopify/cli-kit/node/ui'
import {cwd} from '@shopify/cli-kit/node/path'

vi.mock('../../../utilities/theme-fs.js')
vi.mock('@shopify/cli-kit/node/ui')

describe('GenerateBlock', () => {
const path = cwd()
test('validates theme directory structure by default', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateBlock.run(['--path', path])

// Then
expect(hasRequiredThemeDirectories).toHaveBeenCalledWith(path)
expect(renderWarning).toHaveBeenCalledWith({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
})

test('skips directory validation when force flag is used', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateBlock.run(['--path', path, '--force'])

// Then
expect(hasRequiredThemeDirectories).not.toHaveBeenCalled()
expect(renderWarning).not.toHaveBeenCalled()
})
})
17 changes: 16 additions & 1 deletion packages/theme/src/cli/commands/theme/generate/block.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {themeFlags} from '../../../flags.js'
import ThemeCommand from '../../../utilities/theme-command.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {Flags} from '@oclif/core'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {renderSelectPrompt, renderSuccess, renderTextPrompt} from '@shopify/cli-kit/node/ui'
import {renderSelectPrompt, renderSuccess, renderTextPrompt, renderWarning} from '@shopify/cli-kit/node/ui'

const BLOCK_TYPES = ['text', 'image', 'video', 'product', 'collection']

Expand Down Expand Up @@ -31,11 +32,25 @@ export default class GenerateBlock extends ThemeCommand {
options: [...BLOCK_TYPES],
env: 'SHOPIFY_FLAG_THEME_GENERATE_BLOCK_TYPE',
}),
force: Flags.boolean({
char: 'f',
description: 'Overwrite existing files',
env: 'SHOPIFY_FLAG_THEME_GENERATE_BLOCK_FORCE',
}),
}

async run(): Promise<void> {
const {flags} = await this.parse(GenerateBlock)

if (!flags.force && !(await hasRequiredThemeDirectories(flags.path))) {
renderWarning({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
return
}

const name =
flags.name ??
(await renderTextPrompt({
Expand Down
39 changes: 39 additions & 0 deletions packages/theme/src/cli/commands/theme/generate/section.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import GenerateSection from './section.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {describe, expect, test, vi} from 'vitest'
import {renderWarning} from '@shopify/cli-kit/node/ui'
import {cwd} from '@shopify/cli-kit/node/path'

vi.mock('../../../utilities/theme-fs.js')
vi.mock('@shopify/cli-kit/node/ui')

describe('GenerateSection', () => {
const path = cwd()
test('validates theme directory structure by default', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateSection.run(['--path', path])

// Then
expect(hasRequiredThemeDirectories).toHaveBeenCalledWith(path)
expect(renderWarning).toHaveBeenCalledWith({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
})

test('skips directory validation when force flag is used', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateSection.run(['--path', path, '--force'])

// Then
expect(hasRequiredThemeDirectories).not.toHaveBeenCalled()
expect(renderWarning).not.toHaveBeenCalled()
})
})
17 changes: 16 additions & 1 deletion packages/theme/src/cli/commands/theme/generate/section.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {themeFlags} from '../../../flags.js'
import ThemeCommand from '../../../utilities/theme-command.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {Flags} from '@oclif/core'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {renderSelectPrompt, renderSuccess, renderTextPrompt} from '@shopify/cli-kit/node/ui'
import {renderSelectPrompt, renderSuccess, renderTextPrompt, renderWarning} from '@shopify/cli-kit/node/ui'

const SECTION_TYPES = ['featured-collection', 'image-with-text', 'rich-text', 'custom']

Expand Down Expand Up @@ -31,11 +32,25 @@ export default class GenerateSection extends ThemeCommand {
options: [...SECTION_TYPES],
env: 'SHOPIFY_FLAG_THEME_GENERATE_SECTION_TYPE',
}),
force: Flags.boolean({
char: 'f',
description: 'Overwrite existing files',
env: 'SHOPIFY_FLAG_THEME_GENERATE_SECTION_FORCE',
}),
}

async run(): Promise<void> {
const {flags} = await this.parse(GenerateSection)

if (!flags.force && !(await hasRequiredThemeDirectories(flags.path))) {
renderWarning({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
return
}

const name =
flags.name ??
(await renderTextPrompt({
Expand Down
39 changes: 39 additions & 0 deletions packages/theme/src/cli/commands/theme/generate/template.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import GenerateTemplate from './template.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {describe, expect, test, vi} from 'vitest'
import {renderWarning} from '@shopify/cli-kit/node/ui'
import {cwd} from '@shopify/cli-kit/node/path'

vi.mock('../../../utilities/theme-fs.js')
vi.mock('@shopify/cli-kit/node/ui')

describe('GenerateTemplate', () => {
const path = cwd()
test('validates theme directory structure by default', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateTemplate.run(['--path', path])

// Then
expect(hasRequiredThemeDirectories).toHaveBeenCalledWith(path)
expect(renderWarning).toHaveBeenCalledWith({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
})

test('skips directory validation when force flag is used', async () => {
// Given
vi.mocked(hasRequiredThemeDirectories).mockResolvedValue(false)

// When
await GenerateTemplate.run(['--path', path, '--force'])

// Then
expect(hasRequiredThemeDirectories).not.toHaveBeenCalled()
expect(renderWarning).not.toHaveBeenCalled()
})
})
17 changes: 16 additions & 1 deletion packages/theme/src/cli/commands/theme/generate/template.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {TEMPLATE_TYPES, promptForType} from '../../../utilities/generator.js'
import {themeFlags} from '../../../flags.js'
import ThemeCommand from '../../../utilities/theme-command.js'
import {hasRequiredThemeDirectories} from '../../../utilities/theme-fs.js'
import {Flags} from '@oclif/core'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {renderSuccess, renderTextPrompt} from '@shopify/cli-kit/node/ui'
import {renderSuccess, renderTextPrompt, renderWarning} from '@shopify/cli-kit/node/ui'

export default class GenerateTemplate extends ThemeCommand {
static summary = 'Creates and adds a new template file to your local theme directory'
Expand All @@ -30,11 +31,25 @@ export default class GenerateTemplate extends ThemeCommand {
options: [...TEMPLATE_TYPES],
env: 'SHOPIFY_FLAG_THEME_GENERATE_TEMPLATE_TYPE',
}),
force: Flags.boolean({
char: 'f',
description: 'Overwrite existing files',
env: 'SHOPIFY_FLAG_THEME_GENERATE_TEMPLATE_FORCE',
}),
}

async run(): Promise<void> {
const {flags} = await this.parse(GenerateTemplate)

if (!flags.force && !(await hasRequiredThemeDirectories(flags.path))) {
renderWarning({
body: [
'The current directory does not contain the required theme directories (config, layout, sections, templates).',
],
})
return
}

const name =
flags.name ??
(await renderTextPrompt({
Expand Down

0 comments on commit ff4e09b

Please sign in to comment.