1 |
3a515b92
|
cagy
|
# upath v1.2.0
|
2 |
|
|
|
3 |
|
|
[](https://travis-ci.org/anodynos/upath)
|
4 |
|
|
[](https://david-dm.org/anodynos/upath)
|
5 |
|
|
|
6 |
|
|
A drop-in replacement / proxy to nodejs's `path` that:
|
7 |
|
|
|
8 |
|
|
* Replaces the windows `\` with the unix `/` in all string params & results. This has significant positives - see below.
|
9 |
|
|
|
10 |
|
|
* Adds **filename extensions** functions `addExt`, `trimExt`, `removeExt`, `changeExt`, and `defaultExt`.
|
11 |
|
|
|
12 |
|
|
* Add a `normalizeSafe` function to preserve any meaningful leading `./` & a `normalizeTrim` which additionally trims any useless ending `/`.
|
13 |
|
|
|
14 |
|
|
* Plus a helper `toUnix` that simply converts `\` to `/` and consolidates duplicates.
|
15 |
|
|
|
16 |
|
|
**Useful note: these docs are actually auto generated from [specs](https://github.com/anodynos/upath/blob/master/source/spec/upath-spec.coffee), running on Linux.**
|
17 |
|
|
|
18 |
|
|
Notes:
|
19 |
|
|
|
20 |
|
|
* `upath.sep` is set to `'/'` for seamless replacement (as of 1.0.3).
|
21 |
|
|
|
22 |
|
|
* upath has no runtime dependencies, except built-in `path` (as of 1.0.4)
|
23 |
|
|
|
24 |
|
|
* travis-ci tested in node versions 4 to 12
|
25 |
|
|
|
26 |
|
|
|
27 |
|
|
## Why ?
|
28 |
|
|
|
29 |
|
|
Normal `path` doesn't convert paths to a unified format (ie `/`) before calculating paths (`normalize`, `join`), which can lead to numerous problems.
|
30 |
|
|
Also path joining, normalization etc on the two formats is not consistent, depending on where it runs. Running `path` on Windows yields different results than when it runs on Linux / Mac.
|
31 |
|
|
|
32 |
|
|
In general, if you code your paths logic while developing on Unix/Mac and it runs on Windows, you may run into problems when using `path`.
|
33 |
|
|
|
34 |
|
|
Note that using **Unix `/` on Windows** works perfectly inside nodejs (and other languages), so there's no reason to stick to the Windows legacy at all.
|
35 |
|
|
|
36 |
|
|
##### Examples / specs
|
37 |
|
|
|
38 |
|
|
|
39 |
|
|
Check out the different (improved) behavior to vanilla `path`:
|
40 |
|
|
|
41 |
|
|
`upath.normalize(path)` --returns-->
|
42 |
|
|
|
43 |
|
|
✓ `'c:/windows/nodejs/path'` ---> `'c:/windows/nodejs/path'` // equal to `path.normalize()`
|
44 |
|
|
✓ `'c:/windows/../nodejs/path'` ---> `'c:/nodejs/path'` // equal to `path.normalize()`
|
45 |
|
|
✓ `'c:\\windows\\nodejs\\path'` ---> `'c:/windows/nodejs/path'` // `path.normalize()` gives `'c:\windows\nodejs\path'`
|
46 |
|
|
✓ `'c:\\windows\\..\\nodejs\\path'` ---> `'c:/nodejs/path'` // `path.normalize()` gives `'c:\windows\..\nodejs\path'`
|
47 |
|
|
✓ `'//windows\\unix/mixed'` ---> `'/windows/unix/mixed'` // `path.normalize()` gives `'/windows\unix/mixed'`
|
48 |
|
|
✓ `'\\windows//unix/mixed'` ---> `'/windows/unix/mixed'` // `path.normalize()` gives `'\windows/unix/mixed'`
|
49 |
|
|
✓ `'////\\windows\\..\\unix/mixed/'` ---> `'/unix/mixed/'` // `path.normalize()` gives `'/\windows\..\unix/mixed/'`
|
50 |
|
|
|
51 |
|
|
|
52 |
|
|
Joining paths can also be a problem:
|
53 |
|
|
|
54 |
|
|
`upath.join(paths...)` --returns-->
|
55 |
|
|
|
56 |
|
|
✓ `'some/nodejs/deep', '../path'` ---> `'some/nodejs/path'` // equal to `path.join()`
|
57 |
|
|
✓ `'some/nodejs\\windows', '../path'` ---> `'some/nodejs/path'` // `path.join()` gives `'some/path'`
|
58 |
|
|
✓ `'some\\windows\\only', '..\\path'` ---> `'some/windows/path'` // `path.join()` gives `'some\windows\only/..\path'`
|
59 |
|
|
|
60 |
|
|
|
61 |
|
|
Parsing with `path.parse()` should also be consistent across OSes:
|
62 |
|
|
|
63 |
|
|
`upath.parse(path)` --returns-->
|
64 |
|
|
|
65 |
|
|
✓ `'c:\Windows\Directory\somefile.ext'` ---> `{ root: '', dir: 'c:/Windows/Directory', base: 'somefile.ext', ext: '.ext', name: 'somefile' }`
|
66 |
|
|
// `path.parse()` gives `'{ root: '', dir: '', base: 'c:\\Windows\\Directory\\somefile.ext', ext: '.ext', name: 'c:\\Windows\\Directory\\somefile' }'`
|
67 |
|
|
✓ `'/root/of/unix/somefile.ext'` ---> `{ root: '/', dir: '/root/of/unix', base: 'somefile.ext', ext: '.ext', name: 'somefile' }` // equal to `path.parse()`
|
68 |
|
|
|
69 |
|
|
|
70 |
|
|
## Added functions
|
71 |
|
|
|
72 |
|
|
|
73 |
|
|
#### `upath.toUnix(path)`
|
74 |
|
|
|
75 |
|
|
Just converts all `` to `/` and consolidates duplicates, without performing any normalization.
|
76 |
|
|
|
77 |
|
|
##### Examples / specs
|
78 |
|
|
|
79 |
|
|
`upath.toUnix(path)` --returns-->
|
80 |
|
|
|
81 |
|
|
✓ `'.//windows\//unix//mixed////'` ---> `'./windows/unix/mixed/'`
|
82 |
|
|
✓ `'..///windows\..\\unix/mixed'` ---> `'../windows/../unix/mixed'`
|
83 |
|
|
|
84 |
|
|
|
85 |
|
|
#### `upath.normalizeSafe(path)`
|
86 |
|
|
|
87 |
|
|
Exactly like `path.normalize(path)`, but it keeps the first meaningful `./`.
|
88 |
|
|
|
89 |
|
|
Note that the unix `/` is returned everywhere, so windows `\` is always converted to unix `/`.
|
90 |
|
|
|
91 |
|
|
##### Examples / specs & how it differs from vanilla `path`
|
92 |
|
|
|
93 |
|
|
`upath.normalizeSafe(path)` --returns-->
|
94 |
|
|
|
95 |
|
|
✓ `''` ---> `'.'` // equal to `path.normalize()`
|
96 |
|
|
✓ `'.'` ---> `'.'` // equal to `path.normalize()`
|
97 |
|
|
✓ `'./'` ---> `'./'` // equal to `path.normalize()`
|
98 |
|
|
✓ `'.//'` ---> `'./'` // equal to `path.normalize()`
|
99 |
|
|
✓ `'.\\'` ---> `'./'` // `path.normalize()` gives `'.\'`
|
100 |
|
|
✓ `'.\\//'` ---> `'./'` // `path.normalize()` gives `'.\/'`
|
101 |
|
|
✓ `'./..'` ---> `'..'` // equal to `path.normalize()`
|
102 |
|
|
✓ `'.//..'` ---> `'..'` // equal to `path.normalize()`
|
103 |
|
|
✓ `'./../'` ---> `'../'` // equal to `path.normalize()`
|
104 |
|
|
✓ `'.\\..\\'` ---> `'../'` // `path.normalize()` gives `'.\..\'`
|
105 |
|
|
✓ `'./../dep'` ---> `'../dep'` // equal to `path.normalize()`
|
106 |
|
|
✓ `'../dep'` ---> `'../dep'` // equal to `path.normalize()`
|
107 |
|
|
✓ `'../path/dep'` ---> `'../path/dep'` // equal to `path.normalize()`
|
108 |
|
|
✓ `'../path/../dep'` ---> `'../dep'` // equal to `path.normalize()`
|
109 |
|
|
✓ `'dep'` ---> `'dep'` // equal to `path.normalize()`
|
110 |
|
|
✓ `'path//dep'` ---> `'path/dep'` // equal to `path.normalize()`
|
111 |
|
|
✓ `'./dep'` ---> `'./dep'` // `path.normalize()` gives `'dep'`
|
112 |
|
|
✓ `'./path/dep'` ---> `'./path/dep'` // `path.normalize()` gives `'path/dep'`
|
113 |
|
|
✓ `'./path/../dep'` ---> `'./dep'` // `path.normalize()` gives `'dep'`
|
114 |
|
|
✓ `'.//windows\\unix/mixed/'` ---> `'./windows/unix/mixed/'` // `path.normalize()` gives `'windows\unix/mixed/'`
|
115 |
|
|
✓ `'..//windows\\unix/mixed'` ---> `'../windows/unix/mixed'` // `path.normalize()` gives `'../windows\unix/mixed'`
|
116 |
|
|
✓ `'windows\\unix/mixed/'` ---> `'windows/unix/mixed/'` // `path.normalize()` gives `'windows\unix/mixed/'`
|
117 |
|
|
✓ `'..//windows\\..\\unix/mixed'` ---> `'../unix/mixed'` // `path.normalize()` gives `'../windows\..\unix/mixed'`
|
118 |
|
|
|
119 |
|
|
|
120 |
|
|
#### `upath.normalizeTrim(path)`
|
121 |
|
|
|
122 |
|
|
Exactly like `path.normalizeSafe(path)`, but it trims any useless ending `/`.
|
123 |
|
|
|
124 |
|
|
##### Examples / specs
|
125 |
|
|
|
126 |
|
|
`upath.normalizeTrim(path)` --returns-->
|
127 |
|
|
|
128 |
|
|
✓ `'./'` ---> `'.'` // `upath.normalizeSafe()` gives `'./'`
|
129 |
|
|
✓ `'./../'` ---> `'..'` // `upath.normalizeSafe()` gives `'../'`
|
130 |
|
|
✓ `'./../dep/'` ---> `'../dep'` // `upath.normalizeSafe()` gives `'../dep/'`
|
131 |
|
|
✓ `'path//dep\\'` ---> `'path/dep'` // `upath.normalizeSafe()` gives `'path/dep/'`
|
132 |
|
|
✓ `'.//windows\\unix/mixed/'` ---> `'./windows/unix/mixed'` // `upath.normalizeSafe()` gives `'./windows/unix/mixed/'`
|
133 |
|
|
|
134 |
|
|
|
135 |
|
|
#### `upath.joinSafe([path1][, path2][, ...])`
|
136 |
|
|
|
137 |
|
|
Exactly like `path.join()`, but it keeps the first meaningful `./`.
|
138 |
|
|
|
139 |
|
|
Note that the unix `/` is returned everywhere, so windows `\` is always converted to unix `/`.
|
140 |
|
|
|
141 |
|
|
##### Examples / specs & how it differs from vanilla `path`
|
142 |
|
|
|
143 |
|
|
`upath.joinSafe(path)` --returns-->
|
144 |
|
|
|
145 |
|
|
✓ `'some/nodejs/deep', '../path'` ---> `'some/nodejs/path'` // equal to `path.join()`
|
146 |
|
|
✓ `'./some/local/unix/', '../path'` ---> `'./some/local/path'` // `path.join()` gives `'some/local/path'`
|
147 |
|
|
✓ `'./some\\current\\mixed', '..\\path'` ---> `'./some/current/path'` // `path.join()` gives `'some\current\mixed/..\path'`
|
148 |
|
|
✓ `'../some/relative/destination', '..\\path'` ---> `'../some/relative/path'` // `path.join()` gives `'../some/relative/destination/..\path'`
|
149 |
|
|
|
150 |
|
|
|
151 |
|
|
## Added functions for *filename extension* manipulation.
|
152 |
|
|
|
153 |
|
|
**Happy notes:**
|
154 |
|
|
|
155 |
|
|
In all functions you can:
|
156 |
|
|
|
157 |
|
|
* use both `.ext` & `ext` - the dot `.` on the extension is always adjusted correctly.
|
158 |
|
|
|
159 |
|
|
* omit the `ext` param (pass null/undefined/empty string) and the common sense thing will happen.
|
160 |
|
|
|
161 |
|
|
* ignore specific extensions from being considered as valid ones (eg `.min`, `.dev` `.aLongExtIsNotAnExt` etc), hence no trimming or replacement takes place on them.
|
162 |
|
|
|
163 |
|
|
|
164 |
|
|
|
165 |
|
|
#### `upath.addExt(filename, [ext])`
|
166 |
|
|
|
167 |
|
|
Adds `.ext` to `filename`, but only if it doesn't already have the exact extension.
|
168 |
|
|
|
169 |
|
|
##### Examples / specs
|
170 |
|
|
|
171 |
|
|
`upath.addExt(filename, 'js')` --returns-->
|
172 |
|
|
|
173 |
|
|
✓ `'myfile/addExt'` ---> `'myfile/addExt.js'`
|
174 |
|
|
✓ `'myfile/addExt.txt'` ---> `'myfile/addExt.txt.js'`
|
175 |
|
|
✓ `'myfile/addExt.js'` ---> `'myfile/addExt.js'`
|
176 |
|
|
✓ `'myfile/addExt.min.'` ---> `'myfile/addExt.min..js'`
|
177 |
|
|
|
178 |
|
|
|
179 |
|
|
It adds nothing if no `ext` param is passed.
|
180 |
|
|
|
181 |
|
|
`upath.addExt(filename)` --returns-->
|
182 |
|
|
|
183 |
|
|
✓ `'myfile/addExt'` ---> `'myfile/addExt'`
|
184 |
|
|
✓ `'myfile/addExt.txt'` ---> `'myfile/addExt.txt'`
|
185 |
|
|
✓ `'myfile/addExt.js'` ---> `'myfile/addExt.js'`
|
186 |
|
|
✓ `'myfile/addExt.min.'` ---> `'myfile/addExt.min.'`
|
187 |
|
|
|
188 |
|
|
|
189 |
|
|
#### `upath.trimExt(filename, [ignoreExts], [maxSize=7])`
|
190 |
|
|
|
191 |
|
|
Trims a filename's extension.
|
192 |
|
|
|
193 |
|
|
* Extensions are considered to be up to `maxSize` chars long, counting the dot (defaults to 7).
|
194 |
|
|
|
195 |
|
|
* An `Array` of `ignoreExts` (eg `['.min']`) prevents these from being considered as extension, thus are not trimmed.
|
196 |
|
|
|
197 |
|
|
##### Examples / specs
|
198 |
|
|
|
199 |
|
|
`upath.trimExt(filename)` --returns-->
|
200 |
|
|
|
201 |
|
|
✓ `'my/trimedExt.txt'` ---> `'my/trimedExt'`
|
202 |
|
|
✓ `'my/trimedExt'` ---> `'my/trimedExt'`
|
203 |
|
|
✓ `'my/trimedExt.min'` ---> `'my/trimedExt'`
|
204 |
|
|
✓ `'my/trimedExt.min.js'` ---> `'my/trimedExt.min'`
|
205 |
|
|
✓ `'../my/trimedExt.longExt'` ---> `'../my/trimedExt.longExt'`
|
206 |
|
|
|
207 |
|
|
|
208 |
|
|
It is ignoring `.min` & `.dev` as extensions, and considers exts with up to 8 chars.
|
209 |
|
|
|
210 |
|
|
`upath.trimExt(filename, ['min', '.dev'], 8)` --returns-->
|
211 |
|
|
|
212 |
|
|
✓ `'my/trimedExt.txt'` ---> `'my/trimedExt'`
|
213 |
|
|
✓ `'my/trimedExt.min'` ---> `'my/trimedExt.min'`
|
214 |
|
|
✓ `'my/trimedExt.dev'` ---> `'my/trimedExt.dev'`
|
215 |
|
|
✓ `'../my/trimedExt.longExt'` ---> `'../my/trimedExt'`
|
216 |
|
|
✓ `'../my/trimedExt.longRExt'` ---> `'../my/trimedExt.longRExt'`
|
217 |
|
|
|
218 |
|
|
|
219 |
|
|
#### `upath.removeExt(filename, ext)`
|
220 |
|
|
|
221 |
|
|
Removes the specific `ext` extension from filename, if it has it. Otherwise it leaves it as is.
|
222 |
|
|
As in all upath functions, it be `.ext` or `ext`.
|
223 |
|
|
|
224 |
|
|
##### Examples / specs
|
225 |
|
|
|
226 |
|
|
`upath.removeExt(filename, '.js')` --returns-->
|
227 |
|
|
|
228 |
|
|
✓ `'removedExt.js'` ---> `'removedExt'`
|
229 |
|
|
✓ `'removedExt.txt.js'` ---> `'removedExt.txt'`
|
230 |
|
|
✓ `'notRemoved.txt'` ---> `'notRemoved.txt'`
|
231 |
|
|
|
232 |
|
|
It does not care about the length of exts.
|
233 |
|
|
|
234 |
|
|
`upath.removeExt(filename, '.longExt')` --returns-->
|
235 |
|
|
|
236 |
|
|
✓ `'removedExt.longExt'` ---> `'removedExt'`
|
237 |
|
|
✓ `'removedExt.txt.longExt'` ---> `'removedExt.txt'`
|
238 |
|
|
✓ `'notRemoved.txt'` ---> `'notRemoved.txt'`
|
239 |
|
|
|
240 |
|
|
|
241 |
|
|
#### `upath.changeExt(filename, [ext], [ignoreExts], [maxSize=7])`
|
242 |
|
|
|
243 |
|
|
Changes a filename's extension to `ext`. If it has no (valid) extension, it adds it.
|
244 |
|
|
|
245 |
|
|
* Valid extensions are considered to be up to `maxSize` chars long, counting the dot (defaults to 7).
|
246 |
|
|
|
247 |
|
|
* An `Array` of `ignoreExts` (eg `['.min']`) prevents these from being considered as extension, thus are not changed - the new extension is added instead.
|
248 |
|
|
|
249 |
|
|
##### Examples / specs
|
250 |
|
|
|
251 |
|
|
`upath.changeExt(filename, '.js')` --returns-->
|
252 |
|
|
|
253 |
|
|
✓ `'my/module.min'` ---> `'my/module.js'`
|
254 |
|
|
✓ `'my/module.coffee'` ---> `'my/module.js'`
|
255 |
|
|
✓ `'my/module'` ---> `'my/module.js'`
|
256 |
|
|
✓ `'file/withDot.'` ---> `'file/withDot.js'`
|
257 |
|
|
✓ `'file/change.longExt'` ---> `'file/change.longExt.js'`
|
258 |
|
|
|
259 |
|
|
|
260 |
|
|
If no `ext` param is given, it trims the current extension (if any).
|
261 |
|
|
|
262 |
|
|
`upath.changeExt(filename)` --returns-->
|
263 |
|
|
|
264 |
|
|
✓ `'my/module.min'` ---> `'my/module'`
|
265 |
|
|
✓ `'my/module.coffee'` ---> `'my/module'`
|
266 |
|
|
✓ `'my/module'` ---> `'my/module'`
|
267 |
|
|
✓ `'file/withDot.'` ---> `'file/withDot'`
|
268 |
|
|
✓ `'file/change.longExt'` ---> `'file/change.longExt'`
|
269 |
|
|
|
270 |
|
|
|
271 |
|
|
It is ignoring `.min` & `.dev` as extensions, and considers exts with up to 8 chars.
|
272 |
|
|
|
273 |
|
|
`upath.changeExt(filename, 'js', ['min', '.dev'], 8)` --returns-->
|
274 |
|
|
|
275 |
|
|
✓ `'my/module.coffee'` ---> `'my/module.js'`
|
276 |
|
|
✓ `'file/notValidExt.min'` ---> `'file/notValidExt.min.js'`
|
277 |
|
|
✓ `'file/notValidExt.dev'` ---> `'file/notValidExt.dev.js'`
|
278 |
|
|
✓ `'file/change.longExt'` ---> `'file/change.js'`
|
279 |
|
|
✓ `'file/change.longRExt'` ---> `'file/change.longRExt.js'`
|
280 |
|
|
|
281 |
|
|
|
282 |
|
|
#### `upath.defaultExt(filename, [ext], [ignoreExts], [maxSize=7])`
|
283 |
|
|
|
284 |
|
|
Adds `.ext` to `filename`, only if it doesn't already have _any_ *old* extension.
|
285 |
|
|
|
286 |
|
|
* (Old) extensions are considered to be up to `maxSize` chars long, counting the dot (defaults to 7).
|
287 |
|
|
|
288 |
|
|
* An `Array` of `ignoreExts` (eg `['.min']`) will force adding default `.ext` even if one of these is present.
|
289 |
|
|
|
290 |
|
|
##### Examples / specs
|
291 |
|
|
|
292 |
|
|
`upath.defaultExt(filename, 'js')` --returns-->
|
293 |
|
|
|
294 |
|
|
✓ `'fileWith/defaultExt'` ---> `'fileWith/defaultExt.js'`
|
295 |
|
|
✓ `'fileWith/defaultExt.js'` ---> `'fileWith/defaultExt.js'`
|
296 |
|
|
✓ `'fileWith/defaultExt.min'` ---> `'fileWith/defaultExt.min'`
|
297 |
|
|
✓ `'fileWith/defaultExt.longExt'` ---> `'fileWith/defaultExt.longExt.js'`
|
298 |
|
|
|
299 |
|
|
|
300 |
|
|
If no `ext` param is passed, it leaves filename intact.
|
301 |
|
|
|
302 |
|
|
`upath.defaultExt(filename)` --returns-->
|
303 |
|
|
|
304 |
|
|
✓ `'fileWith/defaultExt'` ---> `'fileWith/defaultExt'`
|
305 |
|
|
✓ `'fileWith/defaultExt.js'` ---> `'fileWith/defaultExt.js'`
|
306 |
|
|
✓ `'fileWith/defaultExt.min'` ---> `'fileWith/defaultExt.min'`
|
307 |
|
|
✓ `'fileWith/defaultExt.longExt'` ---> `'fileWith/defaultExt.longExt'`
|
308 |
|
|
|
309 |
|
|
|
310 |
|
|
It is ignoring `.min` & `.dev` as extensions, and considers exts with up to 8 chars.
|
311 |
|
|
|
312 |
|
|
`upath.defaultExt(filename, 'js', ['min', '.dev'], 8)` --returns-->
|
313 |
|
|
|
314 |
|
|
✓ `'fileWith/defaultExt'` ---> `'fileWith/defaultExt.js'`
|
315 |
|
|
✓ `'fileWith/defaultExt.min'` ---> `'fileWith/defaultExt.min.js'`
|
316 |
|
|
✓ `'fileWith/defaultExt.dev'` ---> `'fileWith/defaultExt.dev.js'`
|
317 |
|
|
✓ `'fileWith/defaultExt.longExt'` ---> `'fileWith/defaultExt.longExt'`
|
318 |
|
|
✓ `'fileWith/defaultExt.longRext'` ---> `'fileWith/defaultExt.longRext.js'`
|
319 |
|
|
|
320 |
|
|
|
321 |
|
|
Copyright(c) 2014-2019 Angelos Pikoulas (agelos.pikoulas@gmail.com)
|
322 |
|
|
|
323 |
|
|
Permission is hereby granted, free of charge, to any person
|
324 |
|
|
obtaining a copy of this software and associated documentation
|
325 |
|
|
files (the "Software"), to deal in the Software without
|
326 |
|
|
restriction, including without limitation the rights to use,
|
327 |
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
328 |
|
|
copies of the Software, and to permit persons to whom the
|
329 |
|
|
Software is furnished to do so, subject to the following
|
330 |
|
|
conditions:
|
331 |
|
|
|
332 |
|
|
The above copyright notice and this permission notice shall be
|
333 |
|
|
included in all copies or substantial portions of the Software.
|
334 |
|
|
|
335 |
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
336 |
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
337 |
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
338 |
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
339 |
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
340 |
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
341 |
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
342 |
|
|
OTHER DEALINGS IN THE SOFTWARE.
|