Skip to content

Commit e48ba5e

Browse files
refactor: address pr comments.
1 parent 49945b4 commit e48ba5e

5 files changed

Lines changed: 132 additions & 32 deletions

File tree

playwright/diagnostics.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,29 @@ test('typecheck resolves parent-relative .js import to workspace tsx module tab'
219219
expect(diagnosticsText).not.toContain("Cannot find module '../components/module.js'")
220220
})
221221

222+
test('typecheck does not report TS2307 for stylesheet side-effect imports', async ({
223+
page,
224+
}) => {
225+
await waitForInitialRender(page)
226+
227+
await ensurePanelToolsVisible(page, 'component')
228+
await setComponentEditorSource(
229+
page,
230+
["import '../styles/app.css'", '', 'const App = () => <p>style import</p>', ''].join(
231+
'\n',
232+
),
233+
)
234+
235+
await runTypecheck(page)
236+
await ensureDiagnosticsDrawerOpen(page)
237+
await expect(page.locator('#diagnostics-component')).toContainText(
238+
'No TypeScript errors found.',
239+
)
240+
241+
const diagnosticsText = await page.locator('#diagnostics-component').innerText()
242+
expect(diagnosticsText).not.toContain("Cannot find module '../styles/app.css'")
243+
})
244+
222245
test('component diagnostics rows navigate editor to reported line', async ({ page }) => {
223246
await waitForInitialRender(page)
224247

playwright/workspace-tabs.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ensurePanelToolsVisible,
55
reorderWorkspaceTabBefore,
66
setWorkspaceTabSource,
7+
waitForAppReady,
78
waitForInitialRender,
89
} from './helpers/app-test-helpers.js'
910

@@ -192,6 +193,21 @@ test('startup restores last active workspace tab after reload', async ({ page })
192193
await expect(page.locator('#editor-panel-styles')).toHaveAttribute('hidden', '')
193194
})
194195

196+
test('removed default styles tab stays removed after reload', async ({ page }) => {
197+
await waitForInitialRender(page)
198+
199+
await expect(page.getByRole('button', { name: 'Open tab app.css' })).toHaveCount(1)
200+
await page.getByRole('button', { name: 'Remove tab app.css' }).click()
201+
await confirmRemoveDialog(page)
202+
203+
await expect(page.getByRole('button', { name: 'Open tab app.css' })).toHaveCount(0)
204+
205+
await page.reload()
206+
await waitForAppReady(page)
207+
208+
await expect(page.getByRole('button', { name: 'Open tab app.css' })).toHaveCount(0)
209+
})
210+
195211
test('workspace tab drag reorder persists across reload', async ({ page }) => {
196212
await waitForInitialRender(page)
197213

src/app.js

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,14 @@ const getWorkspaceTabByKind = kind => {
841841
const getActiveWorkspaceTab = () =>
842842
workspaceTabsState.getTab(workspaceTabsState.getActiveTabId())
843843

844+
const getLoadedComponentWorkspaceTab = () =>
845+
workspaceTabsState.getTab(loadedComponentTabId) ?? getWorkspaceTabByKind('component')
846+
847+
const getTypecheckSourcePath = () => {
848+
const loadedComponentTab = getLoadedComponentWorkspaceTab()
849+
return toNonEmptyWorkspaceText(loadedComponentTab?.path) || defaultComponentTabPath
850+
}
851+
844852
const toStyleModeForTabLanguage = language => {
845853
const normalized = toNonEmptyWorkspaceText(language)
846854
if (normalized === 'less') {
@@ -1054,7 +1062,6 @@ const makeUniqueTabPath = ({ basePath, suffix = '' }) => {
10541062
const ensureWorkspaceTabsShape = tabs => {
10551063
const inputTabs = Array.isArray(tabs) ? tabs : []
10561064
const hasComponent = inputTabs.some(tab => tab?.id === 'component')
1057-
const hasStyles = inputTabs.some(tab => tab?.id === 'styles')
10581065
const nextTabs = [...inputTabs]
10591066

10601067
if (!hasComponent) {
@@ -1069,18 +1076,6 @@ const ensureWorkspaceTabsShape = tabs => {
10691076
})
10701077
}
10711078

1072-
if (!hasStyles) {
1073-
nextTabs.push({
1074-
id: 'styles',
1075-
name: defaultStylesTabName,
1076-
path: defaultStylesTabPath,
1077-
language: 'css',
1078-
role: 'module',
1079-
content: defaultCss,
1080-
isActive: false,
1081-
})
1082-
}
1083-
10841079
return nextTabs.map(tab => {
10851080
if (tab?.id === 'component') {
10861081
const normalizedEntryPath = normalizeEntryTabPath(tab.path, {
@@ -1311,6 +1306,8 @@ const applyWorkspaceRecord = async (workspace, { silent = false } = {}) => {
13111306

13121307
if (typeof stylesTab?.path === 'string' && githubPrStylesPath) {
13131308
githubPrStylesPath.value = stylesTab.path
1309+
} else if (githubPrStylesPath instanceof HTMLInputElement) {
1310+
githubPrStylesPath.value = ''
13141311
}
13151312

13161313
const activeTab = getActiveWorkspaceTab()
@@ -1941,14 +1938,20 @@ const syncTabPathsFromInputs = () => {
19411938
role: 'entry',
19421939
isActive: workspaceTabsState.getActiveTabId() === 'component',
19431940
})
1944-
workspaceTabsState.upsertTab({
1945-
id: 'styles',
1946-
path: stylesPath,
1947-
name: getPathFileName(stylesPath) || defaultStylesTabName,
1948-
language: 'css',
1949-
role: 'module',
1950-
isActive: workspaceTabsState.getActiveTabId() === 'styles',
1951-
})
1941+
1942+
const defaultStylesTab = workspaceTabsState.getTab('styles')
1943+
if (defaultStylesTab) {
1944+
workspaceTabsState.upsertTab({
1945+
id: 'styles',
1946+
path: stylesPath,
1947+
name: getPathFileName(stylesPath) || defaultStylesTabName,
1948+
language: isStyleTabLanguage(defaultStylesTab.language)
1949+
? defaultStylesTab.language
1950+
: 'css',
1951+
role: 'module',
1952+
isActive: workspaceTabsState.getActiveTabId() === 'styles',
1953+
})
1954+
}
19521955

19531956
syncHeaderLabels()
19541957
renderWorkspaceTabs()
@@ -2386,6 +2389,7 @@ const typeDiagnostics = createTypeDiagnosticsController({
23862389
getTypeScriptLibUrls,
23872390
getTypePackageFileUrls,
23882391
getJsxSource: () => getJsxSource(),
2392+
getTypecheckSourcePath,
23892393
getWorkspaceTabs: () => buildWorkspaceTabsSnapshot(),
23902394
getRenderMode: () => renderMode.value,
23912395
setTypecheckButtonLoading,
@@ -2900,6 +2904,7 @@ if (typecheckButton) {
29002904
typeDiagnostics.triggerTypeDiagnostics({
29012905
userInitiated: true,
29022906
source: getJsxSource(),
2907+
sourcePath: getTypecheckSourcePath(),
29032908
})
29042909
})
29052910
}

src/modules/render-runtime.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ export const createRenderRuntimeController = ({
5656
const workspaceGraphCache = createPreviewWorkspaceGraphCache()
5757
const styleTabLanguages = new Set(['css', 'less', 'sass', 'module'])
5858
const stylePathPattern = /\.(?:css|less|sass|scss)$/i
59+
const fallbackEntryTab = {
60+
id: 'component',
61+
name: 'App.tsx',
62+
path: 'src/components/App.tsx',
63+
language: 'javascript-jsx',
64+
role: 'entry',
65+
}
66+
const fallbackStylesTab = {
67+
id: 'styles',
68+
name: 'app.css',
69+
path: 'src/styles/app.css',
70+
language: 'css',
71+
role: 'module',
72+
content: '',
73+
}
5974

6075
const isStyleTab = tab => {
6176
if (!tab || typeof tab !== 'object') {
@@ -477,13 +492,10 @@ export const createRenderRuntimeController = ({
477492

478493
return [
479494
{
480-
id: 'component',
481-
name: 'App.tsx',
482-
path: 'src/components/App.tsx',
483-
language: 'javascript-jsx',
484-
role: 'entry',
495+
...fallbackEntryTab,
485496
content: getJsxSource(),
486497
},
498+
{ ...fallbackStylesTab },
487499
]
488500
}
489501

src/modules/type-diagnostics.js

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ const domJsxTypes =
4949
' interface IntrinsicElements { [elemName: string]: Record<string, unknown> }\n' +
5050
'}\n'
5151

52+
const styleImportTypes =
53+
"declare module '*.css' {\n" +
54+
' const cssText: string\n' +
55+
' export default cssText\n' +
56+
'}\n' +
57+
"declare module '*.less' {\n" +
58+
' const lessText: string\n' +
59+
' export default lessText\n' +
60+
'}\n' +
61+
"declare module '*.sass' {\n" +
62+
' const sassText: string\n' +
63+
' export default sassText\n' +
64+
'}\n' +
65+
"declare module '*.scss' {\n" +
66+
' const scssText: string\n' +
67+
' export default scssText\n' +
68+
'}\n'
5269
const normalizeVirtualFileName = fileName =>
5370
typeof fileName === 'string' && fileName.startsWith('/') ? fileName.slice(1) : fileName
5471

@@ -234,6 +251,7 @@ export const createTypeDiagnosticsController = ({
234251
getTypeScriptLibUrls,
235252
getTypePackageFileUrls,
236253
getJsxSource,
254+
getTypecheckSourcePath = () => '',
237255
getWorkspaceTabs = () => [],
238256
getRenderMode = () => 'dom',
239257
defaultTypeScriptLibFileName = 'lib.esnext.full.d.ts',
@@ -800,12 +818,21 @@ export const createTypeDiagnosticsController = ({
800818
)
801819
}
802820

803-
const collectTypeDiagnostics = async (compiler, sourceText) => {
821+
const collectTypeDiagnostics = async (
822+
compiler,
823+
{ sourceText, sourcePathOverride = '' },
824+
) => {
804825
const workspaceComponentTabs = toWorkspaceComponentTabs()
805826
const resolvedEntryTab = resolveWorkspaceEntryForTypecheck(workspaceComponentTabs)
806-
const sourceFileName = resolvedEntryTab?.path || 'component.tsx'
827+
const normalizedSourcePathOverride =
828+
typeof sourcePathOverride === 'string'
829+
? normalizeRelativePath(sourcePathOverride)
830+
: ''
831+
const sourceFileName =
832+
normalizedSourcePathOverride || resolvedEntryTab?.path || 'component.tsx'
807833
const typecheckSourceFileName = sourceFileName.replace(/\.(jsx?|mjs|cjs)$/i, '.tsx')
808834
const jsxTypesFileName = 'knighted-jsx-runtime.d.ts'
835+
const styleImportTypesFileName = 'knighted-style-imports.d.ts'
809836
const renderMode = getRenderMode()
810837
const isReactMode = renderMode === 'react'
811838
const libFiles = await ensureTypeScriptLibFiles()
@@ -824,6 +851,7 @@ export const createTypeDiagnosticsController = ({
824851
}
825852

826853
files.set(typecheckSourceFileName, sourceText)
854+
files.set(styleImportTypesFileName, styleImportTypes)
827855

828856
if (!isReactMode) {
829857
files.set(jsxTypesFileName, domJsxTypes)
@@ -978,7 +1006,7 @@ export const createTypeDiagnosticsController = ({
9781006
resolveModuleNames,
9791007
}
9801008

981-
const rootNames = [typecheckSourceFileName]
1009+
const rootNames = [typecheckSourceFileName, styleImportTypesFileName]
9821010
if (!isReactMode) {
9831011
rootNames.push(jsxTypesFileName)
9841012
}
@@ -1005,7 +1033,11 @@ export const createTypeDiagnosticsController = ({
10051033

10061034
const runTypeDiagnostics = async (
10071035
runId,
1008-
{ userInitiated = false, sourceOverride = undefined } = {},
1036+
{
1037+
userInitiated = false,
1038+
sourceOverride = undefined,
1039+
sourcePathOverride = undefined,
1040+
} = {},
10091041
) => {
10101042
incrementTypeDiagnosticsRuns()
10111043
setTypeDiagnosticsPending(false)
@@ -1025,7 +1057,14 @@ export const createTypeDiagnosticsController = ({
10251057

10261058
const sourceForRun =
10271059
typeof sourceOverride === 'string' ? sourceOverride : getJsxSource()
1028-
const diagnostics = await collectTypeDiagnostics(compiler, sourceForRun)
1060+
const sourcePathForRun =
1061+
typeof sourcePathOverride === 'string' && sourcePathOverride.length > 0
1062+
? sourcePathOverride
1063+
: getTypecheckSourcePath()
1064+
const diagnostics = await collectTypeDiagnostics(compiler, {
1065+
sourceText: sourceForRun,
1066+
sourcePathOverride: sourcePathForRun,
1067+
})
10291068
const errorCategory = compiler.DiagnosticCategory?.Error
10301069
const errors = diagnostics.filter(
10311070
diagnostic => diagnostic.category === errorCategory,
@@ -1087,11 +1126,16 @@ export const createTypeDiagnosticsController = ({
10871126
}
10881127
}
10891128

1090-
const triggerTypeDiagnostics = ({ userInitiated = false, source = undefined } = {}) => {
1129+
const triggerTypeDiagnostics = ({
1130+
userInitiated = false,
1131+
source = undefined,
1132+
sourcePath = undefined,
1133+
} = {}) => {
10911134
typeCheckRunId += 1
10921135
void runTypeDiagnostics(typeCheckRunId, {
10931136
userInitiated,
10941137
sourceOverride: source,
1138+
sourcePathOverride: sourcePath,
10951139
})
10961140
}
10971141

0 commit comments

Comments
 (0)