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 5b0886b commit 2ef939d
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 3 deletions.
27 changes: 27 additions & 0 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5326,6 +5326,15 @@
"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": "Proceed without confirmation, if current directory does not seem to be theme directory.",
"env": "SHOPIFY_FLAG_FORCE",
"hidden": true,
"name": "force",
"type": "boolean"
},
"name": {
"char": "n",
"description": "Name of the block",
Expand Down Expand Up @@ -5396,6 +5405,15 @@
"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": "Proceed without confirmation, if current directory does not seem to be theme directory.",
"env": "SHOPIFY_FLAG_FORCE",
"hidden": true,
"name": "force",
"type": "boolean"
},
"name": {
"char": "n",
"description": "Name of the section",
Expand Down Expand Up @@ -5465,6 +5483,15 @@
"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": "Proceed without confirmation, if current directory does not seem to be theme directory.",
"env": "SHOPIFY_FLAG_FORCE",
"hidden": true,
"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()
})
})
18 changes: 17 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,26 @@ export default class GenerateBlock extends ThemeCommand {
options: [...BLOCK_TYPES],
env: 'SHOPIFY_FLAG_BLOCK_TYPE',
}),
force: Flags.boolean({
hidden: true,
char: 'f',
description: 'Proceed without confirmation, if current directory does not seem to be theme directory.',
env: 'SHOPIFY_FLAG_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()
})
})
18 changes: 17 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,26 @@ export default class GenerateSection extends ThemeCommand {
options: [...SECTION_TYPES],
env: 'SHOPIFY_FLAG_SECTION_TYPE',
}),
force: Flags.boolean({
hidden: true,
char: 'f',
description: 'Proceed without confirmation, if current directory does not seem to be theme directory.',
env: 'SHOPIFY_FLAG_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()
})
})
18 changes: 17 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,26 @@ export default class GenerateTemplate extends ThemeCommand {
options: [...TEMPLATE_TYPES],
env: 'SHOPIFY_FLAG_TEMPLATE_TYPE',
}),
force: Flags.boolean({
hidden: true,
char: 'f',
description: 'Proceed without confirmation, if current directory does not seem to be theme directory.',
env: 'SHOPIFY_FLAG_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 2ef939d

Please sign in to comment.