1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmldomtop_p.h" |
5 | #include "qqmldomoutwriter_p.h" |
6 | #include "qqmldomcomments_p.h" |
7 | #include "qqmldommock_p.h" |
8 | #include "qqmldomelements_p.h" |
9 | |
10 | #include <QtQml/private/qqmljslexer_p.h> |
11 | #include <QtQml/private/qqmljsparser_p.h> |
12 | #include <QtQml/private/qqmljsengine_p.h> |
13 | #include <QtQml/private/qqmljsastvisitor_p.h> |
14 | #include <QtQml/private/qqmljsast_p.h> |
15 | #include <QtCore/QDir> |
16 | #include <QtCore/QScopeGuard> |
17 | #include <QtCore/QFileInfo> |
18 | #include <QtCore/QRegularExpression> |
19 | #include <QtCore/QRegularExpressionMatch> |
20 | |
21 | #include <algorithm> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | using namespace Qt::StringLiterals; |
26 | |
27 | namespace QQmlJS { |
28 | namespace Dom { |
29 | |
30 | ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path, |
31 | int derivedFrom, QString code) |
32 | : OwningItem(derivedFrom, lastDataUpdateAt), |
33 | m_canonicalFilePath(filePath), |
34 | m_code(code), |
35 | m_path(path) |
36 | {} |
37 | |
38 | QString ExternalOwningItem::canonicalFilePath(DomItem &) const |
39 | { |
40 | return m_canonicalFilePath; |
41 | } |
42 | |
43 | QString ExternalOwningItem::canonicalFilePath() const |
44 | { |
45 | return m_canonicalFilePath; |
46 | } |
47 | |
48 | Path ExternalOwningItem::canonicalPath(DomItem &) const |
49 | { |
50 | return m_path; |
51 | } |
52 | |
53 | Path ExternalOwningItem::canonicalPath() const |
54 | { |
55 | return m_path; |
56 | } |
57 | |
58 | ErrorGroups QmldirFile::myParsingErrors() |
59 | { |
60 | static ErrorGroups res = { .groups: { DomItem::domErrorGroup, NewErrorGroup("Qmldir" ), |
61 | NewErrorGroup("Parsing" ) } }; |
62 | return res; |
63 | } |
64 | |
65 | std::shared_ptr<QmldirFile> QmldirFile::fromPathAndCode(QString path, QString code) |
66 | { |
67 | QString canonicalFilePath = QFileInfo(path).canonicalFilePath(); |
68 | |
69 | QDateTime dataUpdate = QDateTime::currentDateTimeUtc(); |
70 | auto res = std::make_shared<QmldirFile>(args&: canonicalFilePath, args&: code, args&: dataUpdate); |
71 | |
72 | if (canonicalFilePath.isEmpty() && !path.isEmpty()) |
73 | res->addErrorLocal( |
74 | msg: myParsingErrors().error(message: tr(sourceText: "QmldirFile started from invalid path '%1'" ).arg(a: path))); |
75 | res->parse(); |
76 | return res; |
77 | } |
78 | |
79 | void QmldirFile::parse() |
80 | { |
81 | if (canonicalFilePath().isEmpty()) { |
82 | addErrorLocal(msg: myParsingErrors().error(message: tr(sourceText: "canonicalFilePath is empty" ))); |
83 | setIsValid(false); |
84 | } else { |
85 | m_qmldir.parse(source: m_code); |
86 | setFromQmldir(); |
87 | } |
88 | } |
89 | |
90 | void QmldirFile::setFromQmldir() |
91 | { |
92 | m_uri = QmlUri::fromUriString(importStr: m_qmldir.typeNamespace()); |
93 | if (m_uri.isValid()) |
94 | m_uri = QmlUri::fromDirectoryString(importStr: canonicalFilePath()); |
95 | Path exportsPath = Path::Field(s: Fields::exports); |
96 | QDir baseDir = QFileInfo(canonicalFilePath()).dir(); |
97 | int majorVersion = Version::Undefined; |
98 | bool ok; |
99 | int vNr = QFileInfo(baseDir.dirName()).suffix().toInt(ok: &ok); |
100 | if (ok && vNr > 0) // accept 0? |
101 | majorVersion = vNr; |
102 | Path exportSource = canonicalPath(); |
103 | for (auto const &el : m_qmldir.components()) { |
104 | QString exportFilePath = baseDir.filePath(fileName: el.fileName); |
105 | QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath(); |
106 | if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be |
107 | // created where we expect it) |
108 | canonicalExportFilePath = exportFilePath; |
109 | Export exp; |
110 | exp.exportSourcePath = exportSource; |
111 | exp.isSingleton = el.singleton; |
112 | exp.isInternal = el.internal; |
113 | exp.version = |
114 | Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion), |
115 | el.version.hasMinorVersion() ? el.version.minorVersion() : 0); |
116 | exp.typeName = el.typeName; |
117 | exp.typePath = Paths::qmlFileObjectPath(canonicalFilePath: canonicalExportFilePath); |
118 | exp.uri = uri().toString(); |
119 | m_exports.insert(key: exp.typeName, value: exp); |
120 | if (exp.version.majorVersion > 0) |
121 | m_majorVersions.insert(value: exp.version.majorVersion); |
122 | } |
123 | for (auto const &el : m_qmldir.scripts()) { |
124 | QString exportFilePath = baseDir.filePath(fileName: el.fileName); |
125 | QString canonicalExportFilePath = QFileInfo(exportFilePath).canonicalFilePath(); |
126 | if (canonicalExportFilePath.isEmpty()) // file does not exist (yet? assuming it might be |
127 | // created where we expect it) |
128 | canonicalExportFilePath = exportFilePath; |
129 | Export exp; |
130 | exp.exportSourcePath = exportSource; |
131 | exp.isSingleton = true; |
132 | exp.isInternal = false; |
133 | exp.version = |
134 | Version((el.version.hasMajorVersion() ? el.version.majorVersion() : majorVersion), |
135 | el.version.hasMinorVersion() ? el.version.minorVersion() : 0); |
136 | exp.typePath = Paths::jsFilePath(path: canonicalExportFilePath).field(name: Fields::rootComponent); |
137 | exp.uri = uri().toString(); |
138 | exp.typeName = el.nameSpace; |
139 | m_exports.insert(key: exp.typeName, value: exp); |
140 | if (exp.version.majorVersion > 0) |
141 | m_majorVersions.insert(value: exp.version.majorVersion); |
142 | } |
143 | for (QQmlDirParser::Import const &imp : m_qmldir.imports()) { |
144 | QString uri = imp.module; |
145 | bool isAutoImport = imp.flags & QQmlDirParser::Import::Auto; |
146 | Version v; |
147 | if (isAutoImport) |
148 | v = Version(majorVersion, int(Version::Latest)); |
149 | else { |
150 | v = Version((imp.version.hasMajorVersion() ? imp.version.majorVersion() |
151 | : int(Version::Latest)), |
152 | (imp.version.hasMinorVersion() ? imp.version.minorVersion() |
153 | : int(Version::Latest))); |
154 | } |
155 | m_imports.append(t: Import(QmlUri::fromUriString(importStr: uri), v)); |
156 | m_autoExports.append( |
157 | t: ModuleAutoExport { .import: Import(QmlUri::fromUriString(importStr: uri), v), .inheritVersion: isAutoImport }); |
158 | } |
159 | for (QQmlDirParser::Import const &imp : m_qmldir.dependencies()) { |
160 | QString uri = imp.module; |
161 | if (imp.flags & QQmlDirParser::Import::Auto) |
162 | qWarning() << "qmldir contains dependency with auto keyword" ; |
163 | Version v = Version( |
164 | (imp.version.hasMajorVersion() ? imp.version.majorVersion() : int(Version::Latest)), |
165 | (imp.version.hasMinorVersion() ? imp.version.minorVersion() |
166 | : int(Version::Latest))); |
167 | m_imports.append(t: Import(QmlUri::fromUriString(importStr: uri), v)); |
168 | } |
169 | bool hasInvalidTypeinfo = false; |
170 | for (auto const &el : m_qmldir.typeInfos()) { |
171 | QString elStr = el; |
172 | QFileInfo elPath(elStr); |
173 | if (elPath.isRelative()) |
174 | elPath = QFileInfo(baseDir.filePath(fileName: elStr)); |
175 | QString typeInfoPath = elPath.canonicalFilePath(); |
176 | if (typeInfoPath.isEmpty()) { |
177 | hasInvalidTypeinfo = true; |
178 | typeInfoPath = elPath.absoluteFilePath(); |
179 | } |
180 | m_qmltypesFilePaths.append(t: Paths::qmltypesFilePath(path: typeInfoPath)); |
181 | } |
182 | if (m_qmltypesFilePaths.isEmpty() || hasInvalidTypeinfo) { |
183 | // add all type info files in the directory... |
184 | for (QFileInfo const &entry : |
185 | baseDir.entryInfoList(nameFilters: QStringList({ QLatin1String("*.qmltypes" ) }), |
186 | filters: QDir::Filter::Readable | QDir::Filter::Files)) { |
187 | Path p = Paths::qmltypesFilePath(path: entry.canonicalFilePath()); |
188 | if (!m_qmltypesFilePaths.contains(t: p)) |
189 | m_qmltypesFilePaths.append(t: p); |
190 | } |
191 | } |
192 | bool hasErrors = false; |
193 | for (auto const &el : m_qmldir.errors(uri: uri().toString())) { |
194 | ErrorMessage msg = myParsingErrors().errorMessage(msg: el); |
195 | addErrorLocal(msg); |
196 | if (msg.level == ErrorLevel::Error || msg.level == ErrorLevel::Fatal) |
197 | hasErrors = true; |
198 | } |
199 | setIsValid(!hasErrors); // consider it valid also with errors? |
200 | m_plugins = m_qmldir.plugins(); |
201 | } |
202 | |
203 | QList<ModuleAutoExport> QmldirFile::autoExports() const |
204 | { |
205 | return m_autoExports; |
206 | } |
207 | |
208 | void QmldirFile::setAutoExports(const QList<ModuleAutoExport> &autoExport) |
209 | { |
210 | m_autoExports = autoExport; |
211 | } |
212 | |
213 | void QmldirFile::ensureInModuleIndex(DomItem &self, QString uri) |
214 | { |
215 | // ModuleIndex keeps the various sources of types from a given module uri import |
216 | // this method ensures that all major versions that are contained in this qmldir |
217 | // file actually have a ModuleIndex. This is required so that when importing the |
218 | // latest version the correct "lastest major version" is found, for example for |
219 | // qml only modules (qmltypes files also register their versions) |
220 | DomItem env = self.environment(); |
221 | if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) { |
222 | for (int majorV : m_majorVersions) { |
223 | auto mIndex = envPtr->moduleIndexWithUri(self&: env, uri, majorVersion: majorV, lookup: EnvLookup::Normal, |
224 | changeable: Changeable::Writable); |
225 | } |
226 | } |
227 | } |
228 | |
229 | QCborValue pluginData(QQmlDirParser::Plugin &pl, QStringList cNames) |
230 | { |
231 | QCborArray names; |
232 | for (QString n : cNames) |
233 | names.append(value: n); |
234 | return QCborMap({ { QCborValue(QStringView(Fields::name)), pl.name }, |
235 | { QStringView(Fields::path), pl.path }, |
236 | { QStringView(Fields::classNames), names } }); |
237 | } |
238 | |
239 | bool QmldirFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) |
240 | { |
241 | bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor); |
242 | cont = cont && self.dvValueField(visitor, f: Fields::uri, value: uri().toString()); |
243 | cont = cont && self.dvValueField(visitor, f: Fields::designerSupported, value: designerSupported()); |
244 | cont = cont && self.dvReferencesField(visitor, f: Fields::qmltypesFiles, paths: m_qmltypesFilePaths); |
245 | cont = cont && self.dvWrapField(visitor, f: Fields::exports, obj&: m_exports); |
246 | cont = cont && self.dvWrapField(visitor, f: Fields::imports, obj&: m_imports); |
247 | cont = cont && self.dvItemField(visitor, f: Fields::plugins, it: [this, &self]() { |
248 | QStringList cNames = classNames(); |
249 | return self.subListItem(list: List::fromQListRef<QQmlDirParser::Plugin>( |
250 | pathFromOwner: self.pathFromOwner().field(name: Fields::plugins), list&: m_plugins, |
251 | elWrapper: [cNames](DomItem &list, const PathEls::PathComponent &p, |
252 | QQmlDirParser::Plugin &plugin) { |
253 | return list.subDataItem(c: p, value: pluginData(pl&: plugin, cNames)); |
254 | })); |
255 | }); |
256 | // add qmlfiles as map because this way they are presented the same way as |
257 | // the qmlfiles in a directory |
258 | cont = cont && self.dvItemField(visitor, f: Fields::qmlFiles, it: [this, &self]() { |
259 | const QMap<QString, QString> typeFileMap = qmlFiles(); |
260 | return self.subMapItem(map: Map( |
261 | self.pathFromOwner().field(name: Fields::qmlFiles), |
262 | [typeFileMap](DomItem &map, QString typeV) { |
263 | QString path = typeFileMap.value(key: typeV); |
264 | if (path.isEmpty()) |
265 | return DomItem(); |
266 | else |
267 | return map.subReferencesItem( |
268 | c: PathEls::Key(typeV), |
269 | paths: QList<Path>({ Paths::qmlFileObjectPath(canonicalFilePath: path) })); |
270 | }, |
271 | [typeFileMap](DomItem &) { |
272 | return QSet<QString>(typeFileMap.keyBegin(), typeFileMap.keyEnd()); |
273 | }, |
274 | QStringLiteral(u"QList<Reference>" ))); |
275 | }); |
276 | cont = cont && self.dvWrapField(visitor, f: Fields::autoExports, obj&: m_autoExports); |
277 | return cont; |
278 | } |
279 | |
280 | QMap<QString, QString> QmldirFile::qmlFiles() const |
281 | { |
282 | // add qmlfiles as map because this way they are presented the same way as |
283 | // the qmlfiles in a directory which gives them as fileName->list of references to files |
284 | // this is done only to ensure that they are loaded as dependencies |
285 | QMap<QString, QString> res; |
286 | for (const auto &e : m_exports) |
287 | res.insert(key: e.typeName + QStringLiteral(u"-" ) + e.version.stringValue(), |
288 | value: e.typePath[2].headName()); |
289 | return res; |
290 | } |
291 | |
292 | std::shared_ptr<OwningItem> QmlFile::doCopy(DomItem &) const |
293 | { |
294 | auto res = std::make_shared<QmlFile>(args: *this); |
295 | return res; |
296 | } |
297 | |
298 | QmlFile::QmlFile(const QmlFile &o) |
299 | : ExternalOwningItem(o), |
300 | m_engine(o.m_engine), |
301 | m_ast(o.m_ast), |
302 | m_astComments(o.m_astComments), |
303 | m_comments(o.m_comments), |
304 | m_fileLocationsTree(o.m_fileLocationsTree), |
305 | m_components(o.m_components), |
306 | m_pragmas(o.m_pragmas), |
307 | m_imports(o.m_imports), |
308 | m_importScope(o.m_importScope) |
309 | { |
310 | if (m_astComments) |
311 | m_astComments = std::make_shared<AstComments>(args&: *m_astComments); |
312 | } |
313 | |
314 | QmlFile::QmlFile(QString filePath, QString code, QDateTime lastDataUpdateAt, int derivedFrom) |
315 | : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlFilePath(canonicalFilePath: filePath), derivedFrom, |
316 | code), |
317 | m_engine(new QQmlJS::Engine), |
318 | m_astComments(new AstComments(m_engine)), |
319 | m_fileLocationsTree(FileLocations::createTree(basePath: canonicalPath())) |
320 | { |
321 | QQmlJS::Lexer lexer(m_engine.get()); |
322 | lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/true); |
323 | QQmlJS::Parser parser(m_engine.get()); |
324 | m_isValid = parser.parse(); |
325 | for (DiagnosticMessage msg : parser.diagnosticMessages()) |
326 | addErrorLocal(myParsingErrors().errorMessage(msg).withFile(filePath).withPath(m_path)); |
327 | m_ast = parser.ast(); |
328 | } |
329 | |
330 | ErrorGroups QmlFile::myParsingErrors() |
331 | { |
332 | static ErrorGroups res = { .groups: { DomItem::domErrorGroup, NewErrorGroup("QmlFile" ), |
333 | NewErrorGroup("Parsing" ) } }; |
334 | return res; |
335 | } |
336 | |
337 | bool QmlFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) |
338 | { |
339 | bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor); |
340 | cont = cont && self.dvWrapField(visitor, f: Fields::components, obj&: m_components); |
341 | cont = cont && self.dvWrapField(visitor, f: Fields::pragmas, obj&: m_pragmas); |
342 | cont = cont && self.dvWrapField(visitor, f: Fields::imports, obj&: m_imports); |
343 | cont = cont && self.dvWrapField(visitor, f: Fields::importScope, obj&: m_importScope); |
344 | cont = cont && self.dvWrapField(visitor, f: Fields::fileLocationsTree, obj&: m_fileLocationsTree); |
345 | cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj&: m_comments); |
346 | cont = cont && self.dvWrapField(visitor, f: Fields::astComments, obj&: m_astComments); |
347 | return cont; |
348 | } |
349 | |
350 | DomItem QmlFile::field(DomItem &self, QStringView name) |
351 | { |
352 | if (name == Fields::components) |
353 | return self.wrapField(f: Fields::components, obj&: m_components); |
354 | return DomBase::field(self, name); |
355 | } |
356 | |
357 | void QmlFile::addError(DomItem &self, ErrorMessage msg) |
358 | { |
359 | self.containingObject().addError(msg); |
360 | } |
361 | |
362 | void QmlFile::writeOut(DomItem &self, OutWriter &ow) const |
363 | { |
364 | for (DomItem &p : self.field(name: Fields::pragmas).values()) { |
365 | p.writeOut(lw&: ow); |
366 | } |
367 | for (auto i : self.field(name: Fields::imports).values()) { |
368 | i.writeOut(lw&: ow); |
369 | } |
370 | ow.ensureNewline(nNewlines: 2); |
371 | DomItem mainC = self.field(name: Fields::components).key(name: QString()).index(0); |
372 | mainC.writeOut(lw&: ow); |
373 | } |
374 | |
375 | std::shared_ptr<OwningItem> GlobalScope::doCopy(DomItem &self) const |
376 | { |
377 | auto res = std::make_shared<GlobalScope>( |
378 | args: canonicalFilePath(self), args: lastDataUpdateAt(), args: revision()); |
379 | return res; |
380 | } |
381 | |
382 | bool GlobalScope::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) |
383 | { |
384 | bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor); |
385 | return cont; |
386 | } |
387 | |
388 | void QmltypesFile::ensureInModuleIndex(DomItem &self) |
389 | { |
390 | auto it = m_uris.begin(); |
391 | auto end = m_uris.end(); |
392 | DomItem env = self.environment(); |
393 | if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>()) { |
394 | while (it != end) { |
395 | QString uri = it.key(); |
396 | for (int majorV : it.value()) { |
397 | auto mIndex = envPtr->moduleIndexWithUri(self&: env, uri, majorVersion: majorV, lookup: EnvLookup::Normal, |
398 | changeable: Changeable::Writable); |
399 | mIndex->addQmltypeFilePath(p: self.canonicalPath()); |
400 | } |
401 | ++it; |
402 | } |
403 | } |
404 | } |
405 | |
406 | bool QmltypesFile::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) |
407 | { |
408 | bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor); |
409 | cont = cont && self.dvWrapField(visitor, f: Fields::components, obj&: m_components); |
410 | cont = cont && self.dvWrapField(visitor, f: Fields::exports, obj&: m_exports); |
411 | cont = cont && self.dvItemField(visitor, f: Fields::uris, it: [this, &self]() { |
412 | return self.subMapItem(map: Map::fromMapRef<QSet<int>>( |
413 | pathFromOwner: self.pathFromOwner().field(name: Fields::uris), map&: m_uris, |
414 | elWrapper: [](DomItem &map, const PathEls::PathComponent &p, QSet<int> &el) { |
415 | QList<int> l(el.cbegin(), el.cend()); |
416 | std::sort(first: l.begin(), last: l.end()); |
417 | return map.subListItem( |
418 | list: List::fromQList<int>(pathFromOwner: map.pathFromOwner().appendComponent(c: p), list: l, |
419 | elWrapper: [](DomItem &list, const PathEls::PathComponent &p, |
420 | int &el) { return list.subDataItem(c: p, value: el); })); |
421 | })); |
422 | }); |
423 | cont = cont && self.dvWrapField(visitor, f: Fields::imports, obj&: m_imports); |
424 | return cont; |
425 | } |
426 | |
427 | QmlDirectory::QmlDirectory(QString filePath, QStringList dirList, QDateTime lastDataUpdateAt, |
428 | int derivedFrom) |
429 | : ExternalOwningItem(filePath, lastDataUpdateAt, Paths::qmlDirectoryPath(path: filePath), derivedFrom, |
430 | dirList.join(sep: QLatin1Char('\n'))) |
431 | { |
432 | for (QString f : dirList) { |
433 | addQmlFilePath(relativePath: f); |
434 | } |
435 | } |
436 | |
437 | bool QmlDirectory::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor) |
438 | { |
439 | bool cont = ExternalOwningItem::iterateDirectSubpaths(self, visitor); |
440 | cont = cont && self.dvWrapField(visitor, f: Fields::exports, obj&: m_exports); |
441 | cont = cont && self.dvItemField(visitor, f: Fields::qmlFiles, it: [this, &self]() -> DomItem { |
442 | QDir baseDir(canonicalFilePath()); |
443 | return self.subMapItem(map: Map( |
444 | self.pathFromOwner().field(name: Fields::qmlFiles), |
445 | [this, baseDir](DomItem &map, QString key) -> DomItem { |
446 | QList<Path> res; |
447 | auto it = m_qmlFiles.find(key); |
448 | while (it != m_qmlFiles.end() && it.key() == key) { |
449 | res.append(t: Paths::qmlFilePath( |
450 | canonicalFilePath: QFileInfo(baseDir.filePath(fileName: it.value())).canonicalFilePath())); |
451 | ++it; |
452 | } |
453 | return map.subReferencesItem(c: PathEls::Key(key), paths: res); |
454 | }, |
455 | [this](DomItem &) { |
456 | auto keys = m_qmlFiles.keys(); |
457 | return QSet<QString>(keys.begin(), keys.end()); |
458 | }, |
459 | u"List<Reference>"_s )); |
460 | }); |
461 | return cont; |
462 | } |
463 | |
464 | bool QmlDirectory::addQmlFilePath(QString relativePath) |
465 | { |
466 | QRegularExpression qmlFileRe(QRegularExpression::anchoredPattern( |
467 | expression: uR"((?<compName>[a-zA-z0-9_]+)\.(?:qml|qmlannotation))" )); |
468 | QRegularExpressionMatch m = qmlFileRe.match(subject: relativePath); |
469 | if (m.hasMatch() && !m_qmlFiles.values(key: m.captured(name: u"compName" )).contains(str: relativePath)) { |
470 | m_qmlFiles.insert(key: m.captured(name: u"compName" ), value: relativePath); |
471 | Export e; |
472 | QDir dir(canonicalFilePath()); |
473 | QFileInfo fInfo(dir.filePath(fileName: relativePath)); |
474 | e.exportSourcePath = canonicalPath(); |
475 | e.typeName = m.captured(name: u"compName" ); |
476 | e.typePath = Paths::qmlFileObjectPath(canonicalFilePath: fInfo.canonicalFilePath()); |
477 | e.uri = QLatin1String("file://" ) + canonicalFilePath(); |
478 | m_exports.insert(key: e.typeName, value: e); |
479 | return true; |
480 | } |
481 | return false; |
482 | } |
483 | |
484 | } // end namespace Dom |
485 | } // end namespace QQmlJS |
486 | |
487 | QT_END_NAMESPACE |
488 | |