diff --git a/apps/demos/Demos/Charts/MultipleAxes/Vue/App.vue b/apps/demos/Demos/Charts/MultipleAxes/Vue/App.vue index 65d5d0ba4d27..31e03cbb1fab 100644 --- a/apps/demos/Demos/Charts/MultipleAxes/Vue/App.vue +++ b/apps/demos/Demos/Charts/MultipleAxes/Vue/App.vue @@ -70,7 +70,7 @@ function customizeTooltip(pointInfo: DxChartTypes.PointInfo) { const items = pointInfo.valueText?.split('\n'); const color = pointInfo.point?.getColor(); - items?.forEach((item, index) => { + items?.forEach((item: string, index: number) => { if (item.indexOf(pointInfo.seriesName) === 0) { const element = document.createElement('span'); diff --git a/apps/demos/Demos/Diagram/ItemSelection/Vue/App.vue b/apps/demos/Demos/Diagram/ItemSelection/Vue/App.vue index 2f9587c008ef..0c271828b571 100644 --- a/apps/demos/Demos/Diagram/ItemSelection/Vue/App.vue +++ b/apps/demos/Demos/Diagram/ItemSelection/Vue/App.vue @@ -46,7 +46,7 @@ const textExpression = 'Full_Name'; function onContentReady(e: DxDiagramTypes.ContentReadyEvent) { const diagram = e.component; // preselect some shape - const items = diagram.getItems().filter(({ itemType, dataItem }) => itemType === 'shape' && (dataItem[textExpression] === 'Greta Sims')); + const items = diagram.getItems().filter(({ itemType, dataItem }: DxDiagramTypes.Item) => itemType === 'shape' && (dataItem[textExpression] === 'Greta Sims')); if (items.length > 0) { diagram.setSelectedItems(items); diagram.scrollToItem(items[0]); @@ -56,7 +56,7 @@ function onContentReady(e: DxDiagramTypes.ContentReadyEvent) { function onSelectionChanged({ items }: DxDiagramTypes.SelectionChangedEvent) { selectedItemNames.value = 'Nobody has been selected'; const filteredItems = items - .filter((item) => item.itemType === 'shape') + .filter((item: DxDiagramTypes.Item) => item.itemType === 'shape') .map(({ text }: Record) => text); if (filteredItems.length > 0) { selectedItemNames.value = filteredItems.join(', '); diff --git a/apps/demos/Demos/TreeView/DragAndDropHierarchicalDataStructure/Vue/App.vue b/apps/demos/Demos/TreeView/DragAndDropHierarchicalDataStructure/Vue/App.vue index d76877e5223c..57c1c3ab6a62 100644 --- a/apps/demos/Demos/TreeView/DragAndDropHierarchicalDataStructure/Vue/App.vue +++ b/apps/demos/Demos/TreeView/DragAndDropHierarchicalDataStructure/Vue/App.vue @@ -167,7 +167,7 @@ function moveNode( ) { const fromNodeContainingArray = getNodeContainingArray(fromNode, fromItems); const fromIndex = fromNodeContainingArray - ?.findIndex((item) => item.id === fromNode.itemData?.id) || -1; + ?.findIndex((item: DriveItem) => item.id === fromNode.itemData?.id) || -1; if (fromIndex !== -1 && fromNodeContainingArray) { fromNodeContainingArray.splice(fromIndex, 1); @@ -179,7 +179,7 @@ function moveNode( const toNodeContainingArray = getNodeContainingArray(toNode, toItems); const toIndex = toNode === null ? toNodeContainingArray?.length || 0 - : toNodeContainingArray?.findIndex((item) => item.id === toNode.itemData?.id) || 0; + : toNodeContainingArray?.findIndex((item: DriveItem) => item.id === toNode.itemData?.id) || 0; toNodeContainingArray?.splice(toIndex, 0, fromNode.itemData); } } diff --git a/apps/demos/Demos/TreeView/DragAndDropPlainDataStructure/Vue/App.vue b/apps/demos/Demos/TreeView/DragAndDropPlainDataStructure/Vue/App.vue index ca750ebcae7c..8e3c4fce7fa1 100644 --- a/apps/demos/Demos/TreeView/DragAndDropPlainDataStructure/Vue/App.vue +++ b/apps/demos/Demos/TreeView/DragAndDropPlainDataStructure/Vue/App.vue @@ -184,7 +184,7 @@ function moveChildren(node: Node, fromDataSource: DriveItem[], toDataSource: any return; } - node.children?.forEach((child) => { + node.children?.forEach((child: Node) => { if (child.itemData?.isDirectory) { moveChildren(child, fromDataSource, toDataSource); } diff --git a/apps/demos/package.json b/apps/demos/package.json index 5f68340dc040..b8bb429ef6a5 100644 --- a/apps/demos/package.json +++ b/apps/demos/package.json @@ -152,7 +152,7 @@ "testcafe": "3.7.2", "testcafe-reporter-spec-time": "4.0.0", "ts-node": "10.9.2", - "vue-tsc": "^3.0.6" + "vue-tsc": "3.0.6" }, "scripts": { "test": "jest --coverage --runInBand", diff --git a/packages/devextreme/build/gulp/babel-plugin-add-import-extensions.js b/packages/devextreme/build/gulp/babel-plugin-add-import-extensions.js new file mode 100644 index 000000000000..03124f1cf8a5 --- /dev/null +++ b/packages/devextreme/build/gulp/babel-plugin-add-import-extensions.js @@ -0,0 +1,70 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const TS_OUTPUT_BASE_DIR = 'artifacts/dist_ts'; + +module.exports = function addImportExtensions() { + return { + name: 'add-import-extensions', + visitor: { + 'ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration'(astPath) { + const source = astPath.node.source; + + if (!source) return; + + const value = source.value; + + if (!value || (!value.startsWith('./') && !value.startsWith('../'))) { + return; + } + + if (value.match(/\.(js|mjs|json|css)$/)) { + return; + } + + if (value.endsWith('/')) { + source.value = value + 'index.js'; + return; + } + + const currentFile = astPath.hub?.file?.opts?.filename; + const distPathRegExp = new RegExp(TS_OUTPUT_BASE_DIR) + + if (currentFile) { + const currentDir = path.dirname(currentFile); + const resolvedPath = path.resolve(currentDir, value).replace(distPathRegExp,'js'); + + if (fs.existsSync(resolvedPath)) { + const stat = fs.statSync(resolvedPath); + + if (stat.isDirectory()) { + const indexPath = path.join(resolvedPath, 'index.js'); + + if (fs.existsSync(indexPath)) { + source.value = value + '/index.js'; + return; + } + } + } + + let jsFilePath = resolvedPath + '.js'; + + if ( fs.existsSync(jsFilePath) + || fs.existsSync(jsFilePath = resolvedPath + '.ts') + ) { + const stat = fs.statSync(jsFilePath); + + if (stat.isFile()) { + source.value = value + '.js'; + return; + } + } + } + + source.value = value + '.js'; + } + } + }; +}; + diff --git a/packages/devextreme/build/gulp/fix-imports-path.js b/packages/devextreme/build/gulp/fix-imports-path.js new file mode 100644 index 000000000000..b0e55927b4cc --- /dev/null +++ b/packages/devextreme/build/gulp/fix-imports-path.js @@ -0,0 +1,81 @@ +// fix-imports.js +const fs = require('fs'); +const path = require('path'); + +const ROOT_DIR = process.argv[2]; // Папка передаётся первым аргументом + +if (!ROOT_DIR) { + console.error('Использование: node fix-imports.js '); + process.exit(1); +} + +function walkDir(dir, callback) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + walkDir(fullPath, callback); + } else if (entry.isFile() && fullPath.endsWith('.js')) { + callback(fullPath); + } + } +} + +function resolveImport(fileDir, importSpecifier) { + const basePath = path.resolve(fileDir, importSpecifier); + + const candidates = [ + basePath + '.js', + path.join(basePath, 'index.js'), + ]; + + for (const full of candidates) { + if (fs.existsSync(full) && fs.statSync(full).isFile()) { + let rel = path.relative(fileDir, full).replace(/\\/g, '/'); + if (!rel.startsWith('.')) { + rel = './' + rel; + } + return rel; + } + } + return null; +} + +function processFile(filePath) { + const original = fs.readFileSync(filePath, 'utf8'); + let content = original; + const fileDir = path.dirname(filePath); + + const importExportRegex = + /(?:import|export)\s+(?:[^'"]*?\s+from\s+)?(['"])(\.{1,2}\/[^'"]*)\1/g; + + const requireRegex = + /require\(\s*(['"])(\.{1,2}\/[^'"]*)\1\s*\)/g; + + function replaceCallback(_, quote, spec) { + // Если уже есть .js или .mjs — не трогаем + if (spec.endsWith('.js') || spec.endsWith('.mjs')) { + return _; + } + + const resolved = resolveImport(fileDir, spec); + if (!resolved) return _; + + return _.replace(spec, resolved); + } + + content = content.replace(importExportRegex, replaceCallback); + content = content.replace(requireRegex, replaceCallback); + + if (content !== original) { + fs.writeFileSync(filePath, content, 'utf8'); + console.log('Updated:', filePath); + } +} + +; + +module.exports = { + addExtensionToImportsPath: function (dir) { + walkDir(path.resolve(dir), processFile) + }, +}; diff --git a/packages/devextreme/build/gulp/modules_metadata.json b/packages/devextreme/build/gulp/modules_metadata.json index 4bba72e98a25..a25c44c91eaa 100644 --- a/packages/devextreme/build/gulp/modules_metadata.json +++ b/packages/devextreme/build/gulp/modules_metadata.json @@ -661,7 +661,8 @@ "name": "ui/widget/template", "exports": { "Template": { "path": "ui.template", "exportAs": "type" } - } + }, + "types": "./ui/widget/template.d.ts" }, { "name": "utils", @@ -694,6 +695,11 @@ "default": { "path": "viz.dxCircularGauge", "isWidget": true } } }, + { + "name": "viz/common", + "exports": {}, + "types": "./viz/common.d.ts" + }, { "name": "viz/export", "exports": { diff --git a/packages/devextreme/build/gulp/npm.js b/packages/devextreme/build/gulp/npm.js index 9f7f9d5e67bd..c30ddb147076 100644 --- a/packages/devextreme/build/gulp/npm.js +++ b/packages/devextreme/build/gulp/npm.js @@ -11,6 +11,7 @@ const replace = require('gulp-replace'); const lazyPipe = require('lazypipe'); const gulpFilter = require('gulp-filter'); const gulpRename = require('gulp-rename'); +const MODULES = require('./modules_metadata.json'); const compressionPipes = require('./compression-pipes.js'); const ctx = require('./context.js'); @@ -18,6 +19,8 @@ const env = require('./env-variables.js'); const dataUri = require('./gulp-data-uri').gulpPipe; const headerPipes = require('./header-pipes.js'); const { packageDir, packageDistDir, isEsmPackage, stringSrc, devextremeDistDir } = require('./utils'); +const path = require('path'); +const fs = require('fs'); const resultPath = ctx.RESULT_NPM_PATH; @@ -150,6 +153,59 @@ const sources = (src, dist, distGlob) => (() => merge( const packagePath = `${resultPath}/${packageDir}`; const distPath = `${resultPath}/${packageDistDir}`; +function collectExports(baseDir) { + const exportsMap = {}; + + function getPath(p) { + return path.posix.join(p.replace(/\\/g, '/')) + .replace(/^.+\/esm\//, './esm/') + .replace(/^.+\/cjs\//, './cjs/') + } + + function walk(currentDir, relativePath = '.') { + const packageJsonPath = path.join(currentDir, 'package.json'); + + if (fs.existsSync(packageJsonPath) && !/(cjs|esm)$/.test(currentDir)) { + try { + const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + const exportEntry = {}; + + if (pkg.module) { + exportEntry.import = getPath(pkg.module); + } + if (pkg.main) { + exportEntry.require = getPath(pkg.main); + } + if (pkg.typings || pkg.types) { + const typesFile = pkg.typings || pkg.types; + exportEntry.types = path.join(currentDir, typesFile) + .replace(/\\/g, '/') + .replace(/^.*\/devextreme\//, './'); + } + + if (Object.keys(exportEntry).length > 0) { + const exportKey = relativePath === '.' ? '.' : `./${relativePath.replace(/\\/g, '/')}`; + exportsMap[exportKey] = exportEntry; + } + } catch (err) { + console.warn(`Failed to read package.json in ${packageJsonPath}:`, err.message); + } + } + + // проход по вложенным папкам + const entries = fs.readdirSync(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isDirectory()) { + walk(path.join(currentDir, entry.name), path.join(relativePath, entry.name)); + } + } + } + + walk(baseDir); + return exportsMap; +} + gulp.task('npm-sources', gulp.series( 'ts-sources', () => gulp @@ -164,6 +220,33 @@ gulp.task('npm-dist', () => gulp .pipe(gulp.dest(distPath)) ); +gulp.task('add-exports-to-package-json', () => gulp + .src(`${packagePath}/package.json`) + .pipe( + through.obj((file, enc, callback) => { + const pkg = JSON.parse(file.contents.toString(enc)); + +/* pkg.exports = { + "./dist/!*":"./dist/!*", + ...collectExports(path.resolve(packagePath)) + }; + + MODULES.forEach((item) => { + const exportPath = './' + item.name; + if(item.types && !pkg.exports[exportPath]?.types) { + pkg.exports[exportPath] = pkg.exports[exportPath] || {}; + pkg.exports[exportPath].types = item.types; + } + })*/ + + file.contents = Buffer.from(JSON.stringify(pkg, null, 2)); + + callback(null, file); + }) + ) + .pipe(gulp.dest(packagePath)) +); + const scssDir = `${packagePath}/scss`; gulp.task('npm-sass', gulp.series( @@ -183,4 +266,5 @@ gulp.task('npm-sass', gulp.series( ) )); -gulp.task('npm', gulp.series('npm-sources', 'npm-dist', 'ts-check-public-modules', 'npm-sass')); +gulp.task('npm', gulp.series('npm-sources', 'npm-dist', 'ts-check-public-modules', 'npm-sass', 'add-exports-to-package-json')); + diff --git a/packages/devextreme/build/gulp/side-effects-finder.js b/packages/devextreme/build/gulp/side-effects-finder.js index 2df1e4c1ee35..50aa63a2bd82 100644 --- a/packages/devextreme/build/gulp/side-effects-finder.js +++ b/packages/devextreme/build/gulp/side-effects-finder.js @@ -46,7 +46,7 @@ class SideEffectFinder { .forEach((str) => { let importPath = str.match(relativePathRegExp)[0].replace(/(^['"]|['"]$)/g, ''); - importPath = path.join(path.dirname(modulePath), importPath) + '.js'; + importPath = path.join(path.dirname(modulePath), importPath); if(!fs.existsSync(importPath)) { importPath = importPath.replace(/\.js$/, '/index.js'); diff --git a/packages/devextreme/build/gulp/transpile-config.js b/packages/devextreme/build/gulp/transpile-config.js index 9bee0fbd6141..efd7e9aafdd4 100644 --- a/packages/devextreme/build/gulp/transpile-config.js +++ b/packages/devextreme/build/gulp/transpile-config.js @@ -1,5 +1,7 @@ 'use strict'; +const addImportExtensions = require('./babel-plugin-add-import-extensions'); + const common = { plugins: [ ['babel-plugin-inferno', { 'imports': true }], @@ -33,11 +35,12 @@ module.exports = { esm: Object.assign({}, common, { // eslint-disable-next-line spellcheck/spell-checker presets: [['@babel/preset-env', { targets, modules: false }]], - plugins: common.plugins.concat( - [['@babel/plugin-transform-runtime', { + plugins: common.plugins.concat([ + addImportExtensions, + ['@babel/plugin-transform-runtime', { useESModules: true, version: '7.5.0' // https://github.com/babel/babel/issues/10261#issuecomment-514687857 - }]] - ) + }] + ]) }) }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28b59e13386c..ca394469cafc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -696,8 +696,8 @@ importers: specifier: 10.9.2 version: 10.9.2(@swc/core@1.15.3)(@types/node@18.19.64)(typescript@5.9.3) vue-tsc: - specifier: ^3.0.6 - version: 3.0.8(typescript@5.9.3) + specifier: 3.0.6 + version: 3.0.6(typescript@5.9.3) apps/react: dependencies: @@ -8485,8 +8485,8 @@ packages: typescript: optional: true - '@vue/language-core@3.0.8': - resolution: {integrity: sha512-eYs6PF7bxoPYvek9qxceo1BCwFbJZYqJll+WaYC8o8ec60exqj+n+QRGGiJHSeUfYp0hDxARbMdxMq/fbPgU5g==} + '@vue/language-core@3.0.6': + resolution: {integrity: sha512-e2RRzYWm+qGm8apUHW1wA5RQxzNhkqbbKdbKhiDUcmMrNAZGyM8aTiL3UrTqkaFI5s7wJRGGrp4u3jgusuBp2A==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -19618,8 +19618,8 @@ packages: peerDependencies: vue: ^3.2.0 - vue-tsc@3.0.8: - resolution: {integrity: sha512-H9yg/m6ywykmWS+pIAEs65v2FrVm5uOA0a0dHkX6Sx8dNg1a1m4iudt/6eGa9fAenmNHGlLFN9XpWQb8i5sU1w==} + vue-tsc@3.0.6: + resolution: {integrity: sha512-Tbs8Whd43R2e2nxez4WXPvvdjGbW24rOSgRhLOHXzWiT4pcP4G7KeWh0YCn18rF4bVwv7tggLLZ6MJnO6jXPBg==} hasBin: true peerDependencies: typescript: '>=5.0.0' @@ -24327,7 +24327,7 @@ snapshots: '@babel/types@7.27.1': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@babel/types@7.28.5': dependencies: @@ -29309,7 +29309,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/language-core@3.0.8(typescript@5.9.3)': + '@vue/language-core@3.0.6(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.23 '@vue/compiler-dom': 3.5.13 @@ -29318,7 +29318,7 @@ snapshots: alien-signals: 2.0.7 muggle-string: 0.4.1 path-browserify: 1.0.1 - picomatch: 4.0.2 + picomatch: 4.0.3 optionalDependencies: typescript: 5.9.3 @@ -34574,9 +34574,9 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.4.4(picomatch@4.0.2): + fdir@6.4.4(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 fdir@6.5.0(picomatch@4.0.3): optionalDependencies: @@ -43723,8 +43723,8 @@ snapshots: tinyglobby@0.2.13: dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.4.4(picomatch@4.0.3) + picomatch: 4.0.3 tinyglobby@0.2.15: dependencies: @@ -44950,10 +44950,10 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.13(typescript@5.8.3) - vue-tsc@3.0.8(typescript@5.9.3): + vue-tsc@3.0.6(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.23 - '@vue/language-core': 3.0.8(typescript@5.9.3) + '@vue/language-core': 3.0.6(typescript@5.9.3) typescript: 5.9.3 vue@3.2.47: