check-license-header.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #!/usr/bin/env node
  2. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  3. // SPDX-License-Identifier: Apache-2.0
  4. // SPDX-License-Identifier: MIT
  5. const fs = require('fs')
  6. const path = require('path')
  7. const readline = require('readline')
  8. const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  9. SPDX-License-Identifier: Apache-2.0
  10. SPDX-License-Identifier: MIT`
  11. const bundlerLicense =
  12. '// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>'
  13. const extensions = ['.rs', '.js', '.ts', '.yml', '.swift', '.kt']
  14. const ignore = [
  15. 'target',
  16. 'templates',
  17. 'node_modules',
  18. 'gen',
  19. 'dist',
  20. 'bundle.global.js'
  21. ]
  22. async function checkFile(file) {
  23. if (
  24. extensions.some((e) => file.endsWith(e)) &&
  25. !ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
  26. ) {
  27. const fileStream = fs.createReadStream(file)
  28. const rl = readline.createInterface({
  29. input: fileStream,
  30. crlfDelay: Infinity
  31. })
  32. let contents = ``
  33. let i = 0
  34. for await (let line of rl) {
  35. // ignore empty lines, allow shebang and bundler license
  36. if (
  37. line.length === 0 ||
  38. line.startsWith('#!') ||
  39. line.startsWith('// swift-tools-version:') ||
  40. line === bundlerLicense
  41. ) {
  42. continue
  43. }
  44. // strip comment marker
  45. if (line.startsWith('// ')) {
  46. line = line.substring(3)
  47. } else if (line.startsWith('# ')) {
  48. line = line.substring(2)
  49. }
  50. contents += line
  51. if (++i === 3) {
  52. break
  53. }
  54. contents += '\n'
  55. }
  56. if (contents !== header) {
  57. return true
  58. }
  59. }
  60. return false
  61. }
  62. async function check(src) {
  63. const missingHeader = []
  64. for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
  65. const p = path.join(src, entry.name)
  66. if (entry.isSymbolicLink() || ignore.includes(entry.name)) {
  67. continue
  68. }
  69. if (entry.isDirectory()) {
  70. const missing = await check(p)
  71. missingHeader.push(...missing)
  72. } else {
  73. const isMissing = await checkFile(p)
  74. if (isMissing) {
  75. missingHeader.push(p)
  76. }
  77. }
  78. }
  79. return missingHeader
  80. }
  81. const [_bin, _script, ...files] = process.argv
  82. if (files.length > 0) {
  83. async function run() {
  84. const missing = []
  85. for (const f of files) {
  86. const isMissing = await checkFile(f)
  87. if (isMissing) {
  88. missing.push(f)
  89. }
  90. }
  91. if (missing.length > 0) {
  92. console.log(missing.join('\n'))
  93. process.exit(1)
  94. }
  95. }
  96. run()
  97. } else {
  98. check('.').then((missing) => {
  99. if (missing.length > 0) {
  100. console.log(missing.join('\n'))
  101. process.exit(1)
  102. }
  103. })
  104. }